在上一节中利用Ptrace实现了B程序对A程序进程信息的读取,下面一个实例将实现B进程对A进程的运行控制,即利用B进程暂停A进程固定时间后再恢复运行(以操作者手动输入任意字符后恢复执行的方式,A始终恢复失败,报段错误,具体原因请好心人指点)。
【环境】
CentOS 6.6 (Final)
Linux version 2.6.32-504.el6.i686
Gcc version 4.4.7 20120313
【A程序:counter.c】
#include
#include
long long timeum(){
struct timeval tim;
gettimeofday (&tim , NULL);
return (long long)tim.tv_sec*1000000+tim.tv_usec;
}
int main()
{
int i;
long long start,tmp;
start = timeum();
for(i = 0; i < 60; ++i){
printf("My Counter: %d\n", i);
sleep(1);
tmp = timeum();
printf("Time Interval: %lld\n",tmp-start);
start = tmp;
}
return 0;
}
gcc -o counter counter.c
【B程序:pause.c】
#include
#include
#include
#include
#include
#include
const int long_size = sizeof(long);
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*4, NULL);
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*4, 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;
long rst;
i = 0;
j = len / long_size;
laddr = str;
while(i < j){
memcpy(data.chars, laddr, long_size);
rst = ptrace(PTRACE_POKEDATA, child, addr + i*4, data.val);
if (rst < 0) printf("Putdata Failed! \n");
++i;
laddr += long_size;
}
j = len % long_size;
if(j != 0){
memcpy(data.chars, laddr, j);
rst = ptrace(PTRACE_POKEDATA, child, addr + i*4, data.val);
if (rst < 0) printf("Putdata Failed! \n");
}
}
int main(int argc, char *argv[])
{
pid_t traced_process;
struct user_regs_struct regs, newregs;
/* int 0x80, int3 */
char code[] = {0xcd,0x80,0xcc,0};
//char code[] = {0,0,0,0}; //TEST
char backup[4];
if(argc != 2) {
printf("PID?\n");
return 1;
}
traced_process = atoi(argv[1]);
ptrace(PTRACE_ATTACH, traced_process, NULL, NULL);
int pid = wait(NULL);
printf("Attach Pid: %d\n",pid);
ptrace(PTRACE_GETREGS, traced_process, NULL, ®s);
/* Copy instructions into a backup variable */
getdata(traced_process, regs.eip, backup, 3);
/* Put the breakpoint */
putdata(traced_process, regs.eip, code, 3);
/* Let the process continue and execute
the int 3 instruction */
ptrace(PTRACE_CONT, traced_process, NULL, NULL);
wait(NULL);
sleep(5);
/*Segmentation fault (core dumped)
printf("The process stopped, putting back "
"the original instructions ");
printf("Press to continue ");
getchar();*/
putdata(traced_process, regs.eip, backup, 3);
//putdata(traced_process, regs.eip, backup, 3); //TEST
/* Setting the eip back to the original
instruction to let the process continue */
ptrace(PTRACE_SETREGS, traced_process, NULL, ®s);
ptrace(PTRACE_DETACH, traced_process, NULL, NULL);
return 0;
}
gcc -o pause pause.c
【执行】
1. run counter
./counter
2. find pid of counter
ps aux | grep counter
3. run pause(root)
./pause %pid%
【结果】
A进程部分输出如下,从时间间隔上可以明显区分出B程序控制A程序运行的时机。
My Counter: 0
1000217
My Counter: 1
6001265
My Counter: 2
1000604
My Counter: 3
1000585
My Counter: 4
6001558
My Counter: 5
1000564
…
【补充说明】
通过进一步思考,作者认为以上测试只能说明A程序被成功暂停和恢复(只利用ptrace的附加和脱离操作也可实现以上测试结果)。
基于以上测试,作者又做了两项尝试:一是只去除恢复原代码函数(putdata(traced_process, regs.eip, backup, 3)),测试结果发现A程序崩溃,提示Trace/breakpoint trap;二是将替换代码int80/int3 (char code[] = {0,0,0,0})全部修改成零,再去除恢复原代码函数,测试结果发现A程序崩溃,提示Segmentation Fault;
结合以上两项附加测试,作者认为可以充分证明代码注入成功。
【参考】
http://www.cnblogs.com/wangkangluo1/archive/2012/06/05/2535484.html