学了一周的ptrace,很多细节没有研究到。勉强交个自己打及格分的报告吧。希望高手轻喷~
Ptrace是linux提供的系统函数。具体可以在子进程处设置断点。当子进程到达断点时,暂停并通知父进程。此时,父进程可以看到子进程所有内存状态。并且可以直接获取或者修改寄存器等数据。甚至可以插入代码到子进程中。
网上有很多利用ptrace漏洞的。大概思路是说,可以调试超级权限的进程,插入代码到其中,使普通用户具备超级权限。具体感兴趣的同学可以上网学习。
插入代码有两个思路。一个思路是,直接将代码指令插入到子进程eip指向的内存空间。这里需要注意的是,peektext与poketext都是以4个字节为单位,对内存进行读写。所以,如果需要对原来的代码段指令进行备份,应该以4个字节为单位进行备份。不然,可能会在代码中出现乱码。这些乱码对应的指令代码可能会访问没有权限的位置。就会出现段错误。
另一个思路是,将代码指令插入到子进程对应的自由空间中。并且将eip设置到对应的位置,使子进程可以执行替代后的指令。进程被分配的空间,可以在/proc/pid/maps文件里面看到。这里需要注意,如果插入指令,最好找代码段,并且对源代码进行备份。我看到一份资料里面,作者将指令插入到bss段里面,然后将eip设为bss段的起始位置。因为bss段可读可写不可执行,当指令执行到这个线性地址空间,当然因为访问权限不够出现段错误。
下面是一些参数和基本用法。
PTRACE_ME
ptrace(PTRACE_ME,0,0,0)
子进程进入调试模式。
PTRACE_CONT
ptrace(PTRACE_CONT,pid,0,0)
让子进程继续执行,子进程不再进入调试模式。
PTRACE_SYSCALL
ptrace(PTRACE_SYSCALL,pid,0,0)
1、进入系统功能调用之前
2、退出系统功能调用之后
3、子进程结束
PTRACE_SINGLESTEP
Ptrace(PTRACE_SINGLESTEP,pid,0,0)
子进程单步执行
PTRACE_ATTACH
ptrace(PTRACE_ATTACH,pid,0,0)
设置子进程为调试模式。
具体:设置pid对应的子进程为当前进程的子进程。设置子进程为调试模式。
PTRACE_DETACH
解除子进程的调试
PTRACE_PEEKTEXT
PTRACE_POKETEXT
long ptrace(PTRACE_PEEKTEXT, int pid , long offset , 0);
从代码段的offset处取得数据。数据大小是4个字节。
ptrace(PTRACE_POKETEXT, int pid , long offset , long data);
将数据data写入到代码段的offset处。数据大小是4个字节。
PTRACE_PEEKDATA
PTRACE_POKEDATA
long ptrace(PTRACE_PEEKDATA, int pid , long offset , 0);
从数据段的offset处取得数据。数据大小是4个字节。
ptrace(PTRACE_POKEDATA, int pid , long offset , long data);
将数据data写入到数据段的offset处。数据大小是4个字节。
PTRACE_PEEKUSER
PTRACE_POKEUSER
从用户区域取得数据,返回值在offset处取4个字节
long reg = ptrace(PTRACE_PEEKUSER , int pid , long offset ,NULL);
将数据写入到user区。写入的是4个字节。
ptrace(PTRACE_POKEUSER, int pid , long offset , long data);
其中,data是写入的long型数据。
Offset/4可以取EAX(如下)等。
#define EBX 0
#define ECX 1
#define EDX 2
#define ESI 3
#define EDI 4
#define EBP 5
#define EAX 6
#define DS 7
#define ES 8
#define FS 9
#define GS 10
#define ORIG_EAX 11
#define EIP 12
#define CS 13
#define EFL 14
#define UESP 15
#define SS 16
用户区的结构体:
struct user32 {
struct user_regs_struct32 regs; /* Where the registers are actually stored */
int u_fpvalid; /* True if math co-processor being used. */
/* for this mess. Not yet used. */
struct user_i387_ia32_struct i387; /* Math Co-processor registers. */
/* The rest of this junk is to help gdb figure out what goes where */
__u32 u_tsize; /* Text segment size (pages). */
__u32 u_dsize; /* Data segment size (pages). */
__u32 u_ssize; /* Stack segment size (pages). */
__u32 start_code; /* Starting virtual address of text. */
__u32 start_stack; /* Starting virtual address of stack area.
This is actually the bottom of the stack,
the top of the stack is always found in the
esp register. */
__u32 signal; /* Signal that caused the core dump. */
int reserved; /* No __u32er used */
__u32 u_ar0; /* Used by gdb to help find the values for */
/* the registers. */
__u32 u_fpstate; /* Math Co-processor pointer. */
__u32 magic; /* To uniquely identify a core file */
char u_comm[32]; /* User command that was responsible */
int u_debugreg[8];
};
PTRACE_GETREGS
PTRACE_SETREGS
取得寄存器的值
struct user_regs_struct regs;
ptrace(PTRACE_GETREGS , int pid , NULL , & regs);
设置寄存器的值
struct user_regs_struct regs;
ptrace(PTRACE_SETREGS , int pid , NULL , & regs);
struct user_regs_struct {
unsigned long bx;
unsigned long cx;
unsigned long dx;
unsigned long si;
unsigned long di;
unsigned long bp;
unsigned long ax;
unsigned long ds;
unsigned long es;
unsigned long fs;
unsigned long gs;
unsigned long orig_ax;
unsigned long ip;
unsigned long cs;
unsigned long flags;
unsigned long sp;
unsigned long ss;
};
PTRACE_GETFPREGS,
PTRACE_SETFPREGS