first, System call
The operating system provides a series of system call functions to service the application. For more information on system calls, see the << Programmer's Self-discipline chapter 12th.
For the x86 operating system, the interrupt command "int 0x80" is used to make the systems call, before system calls, the system call number needs to be put into the%EAX registers, the system parameters are put into registers%ebx,%ECX,%edx and%esi and%edi in turn.
Take the write system call as an example:
Write (2, "Hello", 5);
In a 32-bit system, it is converted to:
MOVL $1,%eax
movl $2,%ebx
movl $hello,%ecx movl $5,%edx
int $0x80
1 of these are write system call numbers, all system call numbers are defined in the Unistd.h file, $hello represent the address of the string "Hello", and 32-bit Linux systems are called by 0x80 interrupts.
The 64-bit system user Application layer uses the integer register%rdi,%RSI,%RDX,%RCX,%r8 and%R9 to pass the parameter. The kernel interfaces are%rdi,%RSI,%RDX,%R10,&R8, and%r10, and are called with the syscall instruction instead of the 80 interrupt.
Both x86 and x64 use register Rax to save the call number and return value. introduction of Ptrace function
#include <sys/ptrace.h>
long ptrace (enum _ptrace_request request,pid_t pid,void * addr, void *data);
The Ptrace () system call function provides a way for a process (the "tracer") to monitor and control another process (the "Tracee"). And you can check and change the memory and the data in the registers of the "Tracee" process. It can be used to implement breakpoint debugging and system call tracking.
Tracee first need to be attached to tracer. In a multithreaded process, each thread can be attached to a tracer. The Ptrace command is always ptrace (Ptarce_foo,pid,..) Sent to the Tracee process. PID is the Tracee thread ID.
When a process can begin to trace a process to create a subprocess by calling the fork function and have the child process perform ptrace_traceme, then the subprocess calls Execve () (if the current process is successfully executed by PTRACE,EXECVE () The sigtrap semaphore is sent to the process). A process can also use "Ptrace_attach" or "ptrace_seize" to track another process.
When a process is tracked, Tracee pauses whenever the semaphore is transmitted, even if the semaphore is ignored. Tracer will be notified at the next call to Waitpid (Wstatus) (or other wait system calls). The call returns a status code that contains the reason information for the Tracee pause. When Tracee is paused, Tracer can use a series of Ptrace requests to view and modify the information in Tracee. Tracer can then let Tracee continue to execute. The Tracee passed to the tracer is usually ignored.
When the Ptrace_o_traceexec entry does not work, all tracee processes that successfully perform EXECVE () are suspended after a sigtrap semaphore is sent, and the parent process will gain control of the process before the new program executes.
When Tracer ends the trace, you can continue to let Tracee execute by calling Ptrace_detach.
Prace more information can be viewed in the official http://man7.org/linux/man-pages/man2/ptrace.2.html document. third, the example 1.ptrace Tracking subprocess execution exec ()
#include <stdio.h>
#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/ types.h>
#include <sys/wait.h>
#include <sys/reg.h>/ * for Constants Orig_rax etc * *
int main () {
pid_t child;
Long Orig_rax;
Child=fork ();
if (child==0) {
ptrace (ptrace_traceme,0,null,null);
Execl ("/bin/ls", "ls", NULL);
} else{wait
(NULL);
Orig_rax = Ptrace (ptrace_peekuser,child,8*orig_rax,null);
printf ("The child made a system called%ld\n", Orig_rax);
Ptrace (Ptrace_cont,child,null,null);
}
Post-Compile output:
The child made a system call
user1@user-virtual-machine:~/hooktest$ a.out attach.c~ ex1.c EX1.O ex2.c~ ex3.c ex3.o ex4.c~ victim.c~ attach.c attach.o ex1.c~ ex2.c ex2.o ex3.c~ ex4.c victim.c VICTIM.O
The Execl () function corresponds to a system call of __NR_EXECVE, with a system call value of 59. The parent process creates the child process by calling fork (). In a subprocess, run Patrce (). The request parameter is set to Ptrace_trace to tell the kernel that the current process is being TRACE by the parent process, and the process pauses whenever a semaphore is passed to the current process, reminding the parent process to continue at the wait () call. Then call EXECL () again. When the EXECL () function executes successfully, the Sigtrap semaphore is sent to the process to stop the child process before it is run, and the parent process is notified at wait-related call, gets control of the child process, and can view the child process memory and register related information.
When a process makes a system call, int will be in the kernel stack in turn presses the User State Register SS, ESP, EFlags, CS, EIP. The Save_all macro of the interrupt handler pushes the EAX, EBP, EDI, ESI, EDX, ECX, EBX register values into the kernel stack in turn. When calling Ptrace (ptrace_peekuser,child,8*orig_rax,null) to obtain user area information <sys/reg.h> files define subscript with the same order as the kernel stack Register array:
#ifndef _sys_reg_h #define _SYS_REG_H 1 #ifdef __x86_64__/* Index into a array of 8 byte longs returned from Ptrace For location to the users ' stored general purpose registers. * * Define R15 0 # define R14 1 # define R13 2 # define R12 3 # define RBP 4 # define RBX 5 # define R 6 # define R10 7 # define R9 8 # define R8 9 # define RAX # define RCX # define RDX # define RS I # define RDI # define Orig_rax # define RIP # define CS # define EFlags # define RSP 19 # Define SS # define Fs_base # define Gs_base define DS define define FS define GS #else/* Index into a array of 4 byte integers returned from ptrace to * location of the users ' stored general purpose register S. * * Define EBX 0 # define ECX 1 # define EDX 2 # define ESI 3 # define EDI 4 # define EBP 5 # define EAX 6 # define D S 7 # define ES 8 # define FS 9 # define GS # define ORIG_EAX-Define EIP# define CS # define define UESP # define SS #endif
This 8*orig_rax finds the save address for the Orig_rax register value in the user area. Orig_rax saves the system call number.
After the system call is checked, you can call Ptrace and set the parameters Ptrace_cont let the child process continue. 2. Read child process system call parameters
64-bit down chart program #include <sys/ptrace.h> #include <sys/wait.h> #include <sys/reg.h> #include <sys/
user.h> #include <sys/syscall.h> #include <stdio.h> int main () {pid_t child;
Long Orig_rax;
int status;
int iscalling=0;
struct user_regs_struct regs;
Child = fork ();
if (child==0) {ptrace (ptrace_traceme,0,null,null);
Execl ("/bin/ls", "ls", "L", "-H", 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) {ptrace (ptrace_getregs,child,null,®s);
if (!iscalling) {iscalling = 1;
printf ("Sys_write call with%lld,%lld,%lld\n", REGS.RDI,REGS.RSI,REGS.RDX);
else{printf ("Sys_write Call return%lld\n", Regs.rax); iscalling = 0;
} ptrace (Ptrace_syscall,child,null,null);
} return 0; }
Post-Compile output:
Sys_write call with 1, 140179049189376,
total dosage 28K sys_write call to Sys_write call with
1, 14017904918 9376, Wuyi
-rw-rw-r--1 user1 user1 534 February 18:02 ex1.c sys_write call return
Wuyi
sys_write call with 1 , 140179049189376,
-rw-rw-r--1 user1 user1 534 February 18:02 ex1.c~
sys_write call return
SYS _write call with 1, 140179049189376, 2
-rw-rw-r--1 user1 user1 1.1K March 13:02 hook2.c
sys_write cal L return to
Sys_write call with 1, 140179049189376,
-rw-rw-r--1 user1 user1 1.1K March 2 13:02 hook2. c~ sys_write Call "Sys_write call" with
1, 140179049189376,
-rwxrwxr-x 1 user1 user1 8.6K 3 Month 2 13:02 hook2.o
Sys_write Call return 53
You can see that ls-l-H has performed six sys_write system calls.
When reading a parameter in a register, you can use Ptrace_peekuser to read one word, or you can use the Ptrace_getregs parameter to read the value of the register directly into the structure user_regs_struct, which is defined in Sys/user.h
For the Ptrace_stscall parameter, this parameter will continue to execute the paused subprocess as Ptrace_cont, and send the Sintrap semaphore to the subprocess after the next system call, or after the system is tuned, to suspend the child process.
The wifexited function (macro) function is used to check that the child process is paused and is ready to exit. 3. Modify child process system call parameters
val = ptrace (ptrace_peekdata,child,addr,null)
The Ptrace_peekdata, ptrace_peektext parameter is the data that reads a word (sizeof (long) at the addr address of the Tracee memory, and the back value is long and can be read multiple times addr
+i*sizeof (long) and then merges the contents of the final string.
Now, we reverse the string parameters of the system call to the write output:
#include <sys/ptrace.h> #include <sys/wait.h> #include <sys/reg.h> #include <sys/syscall.h> # Include <sys/user.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <
stdlib.h> #define Long_size sizeof (long) void reverse (char * str) {int i,j;
char temp;
for (I=0,j=strlen (str) -2;i<=j;++i,--j) {Temp=str[i];
STR[I]=STR[J];
Str[j]=temp;
} void GetData (pid_t child,long Addr,char * str,int len) {char * LADDR;
int i,j;
Union u{Long Val;
Char Chars[long_size];
} data;
i=0;
J=len/long_size;
LADDR=STR;
while (i<j) {data.val=ptrace (ptrace_peekdata,child,addr+i*long_size,null);
if (Data.val = = 1) {if (errno) {printf ("READ Error:%s\n", Strerror (errno));
} memcpy (Laddr,data.chars,long_size);
++i;
Laddr +=long_size;
};
J=len% Long_size; if (j!=0) {data.val=ptrace (Ptrace_peekdata,child,addr+i*long_size,null);
memcpy (LADDR,DATA.CHARS,J);
} str[len]= ';
} void PutData (pid_t child,long Addr,char * str,int len) {char * LADDR;
int i,j;
Union u{Long Val;
Char Chars[long_size];
} data;
i=0;
J=len/long_size;
LADDR=STR;
while (i<j) {memcpy (data.chars,laddr,long_size);
Ptrace (ptrace_pokedata,child,addr +i*long_size,data.val);
++i;
Laddr+=long_size;
} j=len%long_size; if (j!=0) {//Note: Writes are written by word, so the right thing to do is to read the high address data of the word first and then write it to the high address of data, and then writes the word to memcpy (data.chars,laddr,j)
;
Ptrace (ptrace_pokedata,child,addr +i*long_size,data.val);
int main () {pid_t child;
int status;
struct user_regs_struct regs;
Child =fork ();
if (child ==0) {ptrace (ptrace_traceme,0,null,null);
Execl ("/bin/ls", "ls", NULL);
}else{long Orig_eax; Char *stR,*LADDR;
int toggle = 0;
while (1) {wait (&status);
if (wifexited (status)) break;
Orig_eax = Ptrace (ptrace_peekuser,child,8*orig_rax,null);
if (Orig_eax = = Sys_write) {if (toggle = 0) {toggle = 1;
Ptrace (Ptrace_getregs,child,null,®s);
Str= (char *) calloc (regs.rdx+1), sizeof (char));
GetData (CHILD,REGS.RSI,STR,REGS.RDX);
Reverse (str);
PutData (CHILD,REGS.RSI,STR,REGS.RDX);
}else{Toggle = 0;
} ptrace (Ptrace_syscall,child,null,null);
} return 0;
}
Output:
user1@user-virtual-machine:~/hooktest$./HOOK3.O
o.3kooh ~c.3kooh c.3kooh o.2kooh ~c.2kooh C.2kooh ~c.1xe c.1xe
4. Inject instructions to other programs
When we track other independently running processes, we need to use the following command:
Ptrace (Ptrace_attach, PID, NULL, NULL)
Make the PID process a tracked tracee process. The Tracee process is sent a sigtop semaphore, and the Tracee process does not stop immediately until the system call is complete. If you want to end tracing, call Ptrace_detach.
Debug the ability to set breakpoints can be implemented by Ptrace. The principle is to attach a running process to stop it. It then reads the instruction register IR (32-bit x86 eip,64w is RIP) of the process to the directive that the content points to. After the backup is replaced with the target instruction, then it continues to execute, at which time the tracked process executes our replacement instruction, and after we run the injected instruction, we restore the IR of the original process.
, thus achieving the purpose of changing the logic of the original program operation.
Tracee Process Code:
stdio.h>
int Main () {
int i=0;
while (1) {
printf ("hello,ptrace! [pid:%d]! Num is%d\n ", Getpid (), i++);
Sleep (2);
}
return 0;
}
Tracer Process Code
#include <sys/ptrace.h> #include <sys/reg.h> #include <sys/wait.h> #include <sys/user.h> # include<stdlib.h> #include <errno.h> #include <string.h> #include <stdio.h> #define Long_size
sizeof (long) void GetData (pid_t child, Long addr, char * Str,int len) {char * laddr =str;
int i,j;
Union u{Long Val;
char chars [long_size];
} data;
i=0;
J=len/long_size;
while (i<j) {data.val=ptrace (ptrace_peekdata,child,addr + long_size*i,null);
if (data.val==-1) {if (errno) {printf ("READ Error:%s\n", Strerror (errno));
} memcpy (Laddr,data.chars,long_size);
++i;
Laddr=laddr+long_size;
} j= len%long_size;
if (j!=0) {data.val=ptrace (ptrace_peekdata,child,addr + long_size*i,null); if (data.val==-1) {if (errno) {printf ("READ Error:%s\n", Strerror (errno));
} memcpy (LADDR,DATA.CHARS,J);
} str[len]= ';
} void PutData (pid_t child, Long Addr,char * Str,int len) {char * laddr =str;
int i,j;
J=len/long_size;
i=0;
Union u{Long Val;
char chars [long_size];
} data;
while (i<j) {memcpy (data.chars,laddr,long_size);
Ptrace (ptrace_pokedata,child,addr + long_size*i,data.val);
++i;
Laddr=laddr+long_size;
} j=len%long_size;
if (j!=0) {data.val= ptrace (ptrace_peekdata,child,addr + long_size*i,null);
if (data.val==-1) {if (errno) {printf ("READ Error:%s\n", Strerror (errno));
} memcpy (DATA.CHARS,LADDR,J);
Ptrace (ptrace_pokedata,child,addr + long_size*i,data.val);
int main (int argc,char * argv[]) {if (argc!=2) {printf ("Usage:%s pid\n", argv[0]); } pid_t Tracee = Atoi (argv[1]);
struct user_regs_struct regs; /*int 80 (System call) int 3 (breakpoint) */unsigned char code[]={0xcd,0x80,0xcc,0x00,0,0,0,0}; Eight bytes, equal to long length char backup[8];
Backup read instruction Ptrace (ptrace_attach,tracee,null,null); Long Inst;
Memory address Wait (NULL) to save the next instruction that the instruction register points to;
Ptrace (Ptrace_getregs,tracee,null,®s);
Inst =ptrace (Ptrace_peektext,tracee,regs.rip,null);
printf ("Tracee:rip:0x%llx inst:0x%lx\n", regs.rip,inst);
Read the 7 bytes instructions that the subprocess will execute and back up GetData (tracee,regs.rip,backup,7);
Set Breakpoints PutData (tracee,regs.rip,code,7);
Let the child process continue executing and execute the "INT 3" Breakpoint instruction Stop Ptrace (ptrace_cont,tracee,null,null);
Wait (NULL); Long Rip=ptrace (ptrace_peekuser,tracee,8*rip,null);//When the child process stops, the value of RIP is long inst2=ptrace (Ptrace_peektext,tracee,rip,
NULL);
printf ("Tracee:rip:0x%lx inst:0x%lx\n", rip,inst2);
printf ("Press Enter to continue Tracee process\n");
GetChar (); PutData (tracee,regs.rip,backup,7);
Re-write back the backup instructions to the Register Ptrace (Ptrace_setregs,tracee,null,®s)//setting will be the original register value Ptrace (ptrace_cont,tracee,null,null);
Ptrace (Ptrace_detach,tracee,null,null);
return 0; }
Run the TRACEE.O file first
$ ./TRACEE.O
At this point TRACEE.O output:
hello,ptrace! [pid:14384]! Num is 0
hello,ptrace! [pid:14384]! Num is 1
hello,ptrace! [pid:14384]! Num is 2
hello,ptrace! [pid:14384]! Num is 3 ...
Open another shell and run the ATTACH.O file
$ ./.ATTACH.O 14384//pid
At this point TRACEE.O executes to the INT 3 breakpoint instruction stop, Attach1,o output:
Tracee:rip:0x7f48b0394f20 inst:0x3173fffff0013d48
tracee:rip:0x7f48b0394f23 inst:0x8348c33100000000
Press Enter to continue Tracee process
Press any key TRACEE.O resume execution
Reference:
Http://www.cnblogs.com/pannengzhi/p/5203467.html <