本文为看雪论坛优秀文章
看雪论坛作者ID:有毒
一、简述
ptrace(PTRACE_foom, pid, ...) // pid为linux中对应的线程ID
二、函数原型及初步使用
1. 函数原型
long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
2. 函数定义
SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr,unsigned long, data)
{
struct task_struct *child;
long ret;
if (request == PTRACE_TRACEME) {
ret = ptrace_traceme();
if (!ret)
arch_ptrace_attach(current);
goto out;
}
child = find_get_task_by_vpid(pid);
if (!child) {
ret = -ESRCH;
goto out;
}
if (request == PTRACE_ATTACH || request == PTRACE_SEIZE) {
ret = ptrace_attach(child, request, addr, data);
/*
* Some architectures need to do book-keeping after
* a ptrace attach.
*/
if (!ret)
arch_ptrace_attach(child);
goto out_put_task_struct;
}
ret = ptrace_check_attach(child, request == PTRACE_KILL ||
request == PTRACE_INTERRUPT);
if (ret < 0)
goto out_put_task_struct;
ret = arch_ptrace(child, request, addr, data);
if (ret || request != PTRACE_DETACH)
ptrace_unfreeze_traced(child);
out_put_task_struct:
put_task_struct(child);
out:
return ret;
}
##name, __VA_ARGS__) define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _
##name, __VA_ARGS__) define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _
##name, __VA_ARGS__) define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _
##name, __VA_ARGS__) define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _
##name, __VA_ARGS__) define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _
##name, __VA_ARGS__) define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _
SYSCALL_METADATA(sname, x, __VA_ARGS__)
__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
/*
* The asmlinkage stub is aliased to a function named __se_sys_*() which
* sign-extends 32-bit ints to longs whenever needed. The actual work is
* done within __do_sys_*().
*/
__diag_push();
__diag_ignore(GCC, 8, "-Wattribute-alias",
"Type aliasing is used to sanitize syscall arguments");
asmlinkage long sys
__attribute__((alias(__stringify(__se_sys
ALLOW_ERROR_INJECTION(sys
static inline long __do_sys
asmlinkage long __se_sys
asmlinkage long __se_sys
{
long ret = __do_sys
__MAP(x,__SC_TEST,__VA_ARGS__);
__PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__));
return ret;
}
__diag_pop();
static inline long __do_sys
/* kernel/ptrace.c */
asmlinkage long sys_ptrace(long request, long pid, unsigned long addr,
unsigned long data);
SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr, unsigned long, data)
--> SYSCALL_DEFINEx(4, _ptrace, __VA_ARGS__)
--> __SYSCALL_DEFINEx(4, __ptrace, __VA_ARGS__)
asmlinkage long sys
--> asmlinkage long sys_ptrace(__MAP(4,__SC_DECL,__VA_ARGS__))
/*
* __MAP - apply a macro to syscall arguments
* __MAP(n, m, t1, a1, t2, a2, ..., tn, an) will expand to
* m(t1, a1), m(t2, a2), ..., m(tn, an)
* The first argument must be equal to the amount of type/name
* pairs given. Note that this list of pairs (i.e. the arguments
* of __MAP starting at the third one) is in the same format as
* for SYSCALL_DEFINE<n>/COMPAT_SYSCALL_DEFINE<n>
*/
#define __MAP0(m,...)
#define __MAP1(m,t,a,...) m(t,a)
#define __MAP2(m,t,a,...) m(t,a), __MAP1(m,__VA_ARGS__)
#define __MAP3(m,t,a,...) m(t,a), __MAP2(m,__VA_ARGS__)
#define __MAP4(m,t,a,...) m(t,a), __MAP3(m,__VA_ARGS__)
#define __MAP5(m,t,a,...) m(t,a), __MAP4(m,__VA_ARGS__)
#define __MAP6(m,t,a,...) m(t,a), __MAP5(m,__VA_ARGS__)
#define __MAP(n,...) __MAP##n(__VA_ARGS__)
#define __SC_DECL(t, a) t a
__MAP(4,__SC_DECL, long request, long pid, unsigned long addr,
unsigned long data)
--> __MAP4(__SC_DECL, long, request, long, pid, unsigned long, addr,
unsigned long, data)
--> __SC_DECL(long, request), __MAP3(__SC_DECL, __VA_ARGS__)
__MAP3(__SC_DECL, long, pid, unsigned long, addr, unsigned long, data)
--> __SC_DECL(long, pid), __MAP2(__SC_DECL, unsigned long, addr, unsigned long, data)
-->__SC_DECL(unsigned long, addr), __MAP1(__SC_DECL, __VA_ARGS__)
unsigned long addr, __SC_DECL(unsigned long, data)
--> unsigned long data
long pid, __SC_DECL(unsigned long, addr), __MAP1(__SC_DECL, __VA_ARGS__)
--> long pid, unsigned long addr, unsigned long data
--> long request, __SC_DECL(long, pid), __MAP2(__SC_DECL, __VA_ARGS__)
--> long request, long pid, unsigned long addr, unsigned long data
asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__))
{
long ret = __do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));
__MAP(x,__SC_TEST,__VA_ARGS__);
__PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__));
return ret;
}
define __TYPE_AS(t, v) __same_type((__force t)0, v) /*判断t和v是否是同一个类型*/
define __TYPE_IS_L(t) (__TYPE_AS(t, 0L)) /*判断t是否是long 类型,是返回1*/
define __TYPE_IS_UL(t) (__TYPE_AS(t, 0UL)) /*判断t是否是unsigned long 类型,是返回1*/
define __TYPE_IS_LL(t) (__TYPE_AS(t, 0LL) || __TYPE_AS(t, 0ULL)) /*是long类型就返回1*/
define __SC_LONG(t, a) __typeof(__builtin_choose_expr(__TYPE_IS_LL(t), 0LL, 0L)) a /*将参数转换成long类型*/
define __SC_CAST(t, a) (__force t) a /*转成成原来的类型*/
define __force __attribute__((force)) /*表示所定义的变量类型可以做强制类型转换*/
3. 初步使用
(1)最简单的ls跟踪
int main(int argc, char *argv[]){
pid_t child;
long orig_rax;
child = fork();
if(child == 0){
ptrace(PTRACE_TRACEME, 0, NULL, NULL); // Tell kernel, trace me
execl("/bin/ls", "ls", NULL);
}else{
/*Receive certification after child process stopped*/
wait(NULL);
/*Read child process's rax*/
orig_rax = ptrace(PTRACE_PEEKUSER, child, 8*ORIG_RAX, NULL);
printf("[+] The child made a system call %ld.n", orig_rax);
/*Continue*/
ptrace(PTRACE_CONT, child, NULL, NULL);
}
return 0;
}
(2)系统调用查看参数
int main(int argc, char *argv[]){
pid_t child;
long orig_rax, rax;
long params[3];
int status;
int insyscall = 0;
child = fork();
if(child == 0){
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
execl("/bin/ls", "ls", NULL);
}else{
while(1){
wait(&status);
if(WIFEXITED(status))
break;
orig_rax = ptrace(PTRACE_PEEKUSER, child, 8 * ORIG_RAX, NULL);
if(orig_rax == SYS_write){
if(insyscall == 0){
insyscall = 1;
params[0] = ptrace(PTRACE_PEEKUSER, child, 8 * RBX, NULL);
params[1] = ptrace(PTRACE_PEEKUSER, child, 8 * RCX, NULL);
params[2] = ptrace(PTRACE_PEEKUSER, child, 8 * RDX, NULL);
printf("Write called with %ld, %ld, %ldn", params[0], params[1], params[2]);
}else{
rax = ptrace(PTRACE_PEEKUSER, child, 8 * RAX, NULL);
printf("Write returned with %ldn", rax);
insyscall = 0;
}
}
ptrace(PTRACE_SYSCALL, child, NULL, NULL);
}
}
return 0;
}
(3)系统调用参数-改进版
int main(int argc, char *argv[]){
pid_t child;
long orig_rax, rax;
long params[3];
int status;
int insyscall = 0;
struct user_regs_struct regs;
child = fork();
if(child == 0){
ptrace(PTRACE_TRACEME, child, 8 * ORIG_RAX, NULL);
execl("/bin/ls", "ls", NULL);
}
else{
while(1){
wait(&status);
if(WIFEXITED(status))
break;
orig_rax = ptrace(PTRACE_PEEKUSER, child, 8*ORIG_RAX, NULL);
if(orig_rax == SYS_write){
if(insyscall == 0){
insyscall == 1;
ptrace(PTRACE_GETREGS, child, NULL, ®s);
printf("Write called with %lld, %lld, %lldn", regs.rbx, regs.rcx, regs.rdx);
}else{
rax = ptrace(PTRACE_PEEKUSER, child, 8*rax, NULL);
printf("Write returned with %ldn", rax);
insyscall = 0;
}
}
ptrace(PTRACE_SYSCALL, child, NULL, NULL);
}
}
return 0;
}
参考文献
看雪ID:有毒
https://bbs.pediy.com/user-home-7797730.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!
本文始发于微信公众号(看雪学院):Linux ptrace详细分析系列(一)
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论