青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

elva

Playing with ptrace, Part II

In Part I of this article [LJ, November 2002], we saw how ptrace can be used to trace system calls and change system call arguments. In this article, we investigate advanced techniques like setting breakpoints and injecting code into running programs. Debuggers use these methods to set up breakpoints and execute debugging handlers. As with Part I, all code in this article is i386 architecture-specific.

In Part I, we ran the process to be traced as a child after calling ptrace(PTRACE_TRACEME, ..). If you simply wanted to see how the process is making system calls and trace the program, this would be sufficient. If you want to trace or debug a process already running, then ptrace(PTRACE_ATTACH, ..) should be used.

When a ptrace(PTRACE_ATTACH, ..) is called with the pid to be traced, it is roughly equivalent to the process calling ptrace(PTRACE_TRACEME, ..) and becoming a child of the tracing process. The traced process is sent a SIGSTOP, so we can examine and modify the process as usual. After we are done with modifications or tracing, we can let the traced process continue on its own by calling ptrace(PTRACE_DETACH, ..).

The following is the code for a small example tracing program:

int main()
{ int i;
for(i = 0;i < 10; ++i) {
printf("My counter: %d\n", i);
sleep(2);
}
return 0;
}

Save the program as dummy2.c. Compile and run it:

gcc -o dummy2 dummy2.c
./dummy2 &
Now, we can attach to dummy2 by using the code below:
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <linux/user.h> /* For user_regs_struct
etc. */
int main(int argc, char *argv[])
{ pid_t traced_process;
struct user_regs_struct regs;
long ins;
if(argc != 2) {
printf("Usage: %s <pid to be traced>\n",
argv[0], argv[1]);
exit(1);
}
traced_process = atoi(argv[1]);
ptrace(PTRACE_ATTACH, traced_process,
NULL, NULL);
wait(NULL);
ptrace(PTRACE_GETREGS, traced_process,
NULL, &regs);
ins = ptrace(PTRACE_PEEKTEXT, traced_process,
regs.eip, NULL);
printf("EIP: %lx Instruction executed: %lx\n",
regs.eip, ins);
ptrace(PTRACE_DETACH, traced_process,
NULL, NULL);
return 0;
}
The above program simply attaches to a process, waits for it to stop, examines its eip (instruction pointer) and detaches.

To inject code use ptrace(PTRACE_POKETEXT, ..) and ptrace(PTRACE_POKEDATA, ..) after the traced process has stopped.

Setting Breakpoints

How do debuggers set breakpoints? Generally, they replace the instruction to be executed with a trap instruction, so that when the traced program stops, the tracing program, the debugger, can examine it. It will replace the original instruction once the tracing program continues the traced process. Here's an example:

#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <linux/user.h>
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] = '\0';
}
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 * 4, data.val);
++i;
laddr += long_size;
}
j = len % long_size;
if(j != 0) {
memcpy(data.chars, laddr, j);
ptrace(PTRACE_POKEDATA, child,
addr + i * 4, data.val);
}
}
int main(int argc, char *argv[])
{ pid_t traced_process;
struct user_regs_struct regs, newregs;
long ins;
/* int 0x80, int3 */
char code[] = {0xcd,0x80,0xcc,0};
char backup[4];
if(argc != 2) {
printf("Usage: %s <pid to be traced>\n",
argv[0], argv[1]);
exit(1);
}
traced_process = atoi(argv[1]);
ptrace(PTRACE_ATTACH, traced_process,
NULL, NULL);
wait(NULL);
ptrace(PTRACE_GETREGS, traced_process,
NULL, &regs);
/* 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);
printf("The process stopped, putting back "
"the original instructions\n");
printf("Press <enter> to continue\n");
getchar();
putdata(traced_process, regs.eip, backup, 3);
/* Setting the eip back to the original
instruction to let the process continue */
ptrace(PTRACE_SETREGS, traced_process,
NULL, &regs);
ptrace(PTRACE_DETACH, traced_process,
NULL, NULL);
return 0;
}

Here we replace the three bytes with the code for a trap instruction, and when the process stops, we replace the original instructions and reset the eip to original location. Figures 1-4 clarify how the instruction stream looks when above program is executed.

Figure 1. After the Process Is Stopped

Figure 2. After the Trap Instruction Bytes Are Set

Figure 3. Trap Is Hit and Control Is Given to the Tracing Program

Figure 4. After the Original Instructions Are Replaced and eip Is Reset to the Original Location

Now that we have a clear idea of how breakpoints are set, let's inject some code bytes into a running program. These code bytes will print “hello world”.

The following program is a simple “hello world” program with modifications to fit our needs. Compile the following program with:

gcc -o hello hello.c
void main()
{
__asm__("
jmp forward
backward:
popl %esi # Get the address of
# hello world string
movl $4, %eax # Do write system call
movl $2, %ebx
movl %esi, %ecx
movl $12, %edx
int $0x80
int3 # Breakpoint. Here the
# program will stop and
# give control back to
# the parent
forward:
call backward
.string \"Hello World\\n\""
);
}

The jumping backward and forward here is required to find the address of the “hello world” string.

We can get the machine code for the above assembly from GDB. Fire up GDB and disassemble the program:

(gdb) disassemble main
Dump of assembler code for function main:
0x80483e0 <main>: push %ebp
0x80483e1 <main+1>: mov %esp,%ebp
0x80483e3 <main+3>: jmp 0x80483fa <forward>
End of assembler dump.
(gdb) disassemble forward
Dump of assembler code for function forward:
0x80483fa <forward>: call 0x80483e5 <backward>
0x80483ff <forward+5>: dec %eax
0x8048400 <forward+6>: gs
0x8048401 <forward+7>: insb (%dx),%es:(%edi)
0x8048402 <forward+8>: insb (%dx),%es:(%edi)
0x8048403 <forward+9>: outsl %ds:(%esi),(%dx)
0x8048404 <forward+10>: and %dl,0x6f(%edi)
0x8048407 <forward+13>: jb 0x8048475
0x8048409 <forward+15>: or %fs:(%eax),%al
0x804840c <forward+18>: mov %ebp,%esp
0x804840e <forward+20>: pop %ebp
0x804840f <forward+21>: ret
End of assembler dump.
(gdb) disassemble backward
Dump of assembler code for function backward:
0x80483e5 <backward>: pop %esi
0x80483e6 <backward+1>: mov $0x4,%eax
0x80483eb <backward+6>: mov $0x2,%ebx
0x80483f0 <backward+11>: mov %esi,%ecx
0x80483f2 <backward+13>: mov $0xc,%edx
0x80483f7 <backward+18>: int $0x80
0x80483f9 <backward+20>: int3
End of assembler dump.

We need to take the machine code bytes from main+3 to backward+20, which is a total of 41 bytes. The machine code can be seen with the x command in GDB:

(gdb) x/40bx main+3
<main+3>: eb 15 5e b8 04 00 00 00
<backward+6>: bb 02 00 00 00 89 f1 ba
<backward+14>: 0c 00 00 00 cd 80 cc
<forward+1>: e6 ff ff ff 48 65 6c 6c
<forward+9>: 6f 20 57 6f 72 6c 64 0a
Now we have the instruction bytes to be executed. Why wait? We can inject them using the same method as in the previous example. The following is the source code; only the main function is given here:
int main(int argc, char *argv[])
{ pid_t traced_process;
struct user_regs_struct regs, newregs;
long ins;
int len = 41;
char insertcode[] =
"\xeb\x15\x5e\xb8\x04\x00"
"\x00\x00\xbb\x02\x00\x00\x00\x89\xf1\xba"
"\x0c\x00\x00\x00\xcd\x80\xcc\xe8\xe6\xff"
"\xff\xff\x48\x65\x6c\x6c\x6f\x20\x57\x6f"
"\x72\x6c\x64\x0a\x00";
char backup[len];
if(argc != 2) {
printf("Usage: %s <pid to be traced>\n",
argv[0], argv[1]);
exit(1);
}
traced_process = atoi(argv[1]);
ptrace(PTRACE_ATTACH, traced_process,
NULL, NULL);
wait(NULL);
ptrace(PTRACE_GETREGS, traced_process,
NULL, &regs);
getdata(traced_process, regs.eip, backup, len);
putdata(traced_process, regs.eip,
insertcode, len);
ptrace(PTRACE_SETREGS, traced_process,
NULL, &regs);
ptrace(PTRACE_CONT, traced_process,
NULL, NULL);
wait(NULL);
printf("The process stopped, Putting back "
"the original instructions\n");
putdata(traced_process, regs.eip, backup, len);
ptrace(PTRACE_SETREGS, traced_process,
NULL, &regs);
printf("Letting it continue with "
"original flow\n");
ptrace(PTRACE_DETACH, traced_process,
NULL, NULL);
return 0;
}
Injecting the Code into Free Space

In the previous example we injected the code directly into the executing instruction stream. However, debuggers can get confused with this kind of behaviour, so let's find the free space in the process and inject the code there. We can find free space by examining the /proc/pid/maps file of the traced process. The following function will find the starting address of this map:

long freespaceaddr(pid_t pid)
{
FILE *fp;
char filename[30];
char line[85];
long addr;
char str[20];
sprintf(filename, "/proc/%d/maps", pid);
fp = fopen(filename, "r");
if(fp == NULL)
exit(1);
while(fgets(line, 85, fp) != NULL) {
sscanf(line, "%lx-%*lx %*s %*s %s", &addr,
str, str, str, str);
if(strcmp(str, "00:00") == 0)
break;
}
fclose(fp);
return addr;
}

Each line in /proc/pid/maps represents a mapped region of the process. An entry in /proc/pid/maps looks like this:

map start-mapend    protection  offset     device
inode process file
08048000-0804d000 r-xp 00000000 03:08
66111 /opt/kde2/bin/kdeinit
The following program injects code into free space. It's similar to the previous injection program except the free space address is used for keeping our new code. Here is the source code for the main function:
int main(int argc, char *argv[])
{ pid_t traced_process;
struct user_regs_struct oldregs, regs;
long ins;
int len = 41;
char insertcode[] =
"\xeb\x15\x5e\xb8\x04\x00"
"\x00\x00\xbb\x02\x00\x00\x00\x89\xf1\xba"
"\x0c\x00\x00\x00\xcd\x80\xcc\xe8\xe6\xff"
"\xff\xff\x48\x65\x6c\x6c\x6f\x20\x57\x6f"
"\x72\x6c\x64\x0a\x00";
char backup[len];
long addr;
if(argc != 2) {
printf("Usage: %s <pid to be traced>\n",
argv[0], argv[1]);
exit(1);
}
traced_process = atoi(argv[1]);
ptrace(PTRACE_ATTACH, traced_process,
NULL, NULL);
wait(NULL);
ptrace(PTRACE_GETREGS, traced_process,
NULL, &regs);
addr = freespaceaddr(traced_process);
getdata(traced_process, addr, backup, len);
putdata(traced_process, addr, insertcode, len);
memcpy(&oldregs, &regs, sizeof(regs));
regs.eip = addr;
ptrace(PTRACE_SETREGS, traced_process,
NULL, &regs);
ptrace(PTRACE_CONT, traced_process,
NULL, NULL);
wait(NULL);
printf("The process stopped, Putting back "
"the original instructions\n");
putdata(traced_process, addr, backup, len);
ptrace(PTRACE_SETREGS, traced_process,
NULL, &oldregs);
printf("Letting it continue with "
"original flow\n");
ptrace(PTRACE_DETACH, traced_process,
NULL, NULL);
return 0;
}
Behind the Scenes

So what happens within the kernel now? How is ptrace implemented? This section could be an article on its own; however, here's a brief description of what happens.

When a process calls ptrace with PTRACE_TRACEME, the kernel sets up the process flags to reflect that it is being traced:

Source: arch/i386/kernel/ptrace.c
if (request == PTRACE_TRACEME) {
/* are we already being traced? */
if (current->ptrace & PT_PTRACED)
goto out;
/* set the ptrace bit in the process flags. */
current->ptrace |= PT_PTRACED;
ret = 0;
goto out;
}

When a system call entry is done, the kernel checks this flag and calls the trace system call if the process is being traced. The gory assembly details can be found in arch/i386/kernel/entry.S.

Now, we are in the sys_trace() function as defined in arch/i386/kernel/ptrace.c. It stops the child and sends a signal to the parent notifying that the child is stopped. This wakes up the waiting parent, and it does the ptrace magic. Once the parent is done, and it calls ptrace(PTRACE_CONT, ..) or ptrace(PTRACE_SYSCALL, ..), it wakes up the child by calling the scheduler function wake_up_process(). Some other architectures can implement this by sending a SIGCHLD to child.

Conclusion

ptrace may appear to be magic to some people, because it can examine and modify a running program. It is generally used by debuggers and system call tracing programs, such as ptrace. It opens up interesting possibilities for doing user-mode extensions as well. There have been a lot of attempts to extend the operating system on the user level. See Resources to read about UFO, a user-level extension to filesystems. ptrace also is used to employ security mechanisms.

All example code from this article and from Part I is available as a tar archive on the Linux Journal FTP site [ftp.ssc.com/pub/lj/listings/issue104/6210.tgz].


原文:

http://www.linuxjournal.com/article/6210

posted on 2009-07-25 17:45 葉子 閱讀(619) 評論(0)  編輯 收藏 引用 所屬分類: Unix

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
      <noscript id="pjuwb"></noscript>
            <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
              <dd id="pjuwb"></dd>
              <abbr id="pjuwb"></abbr>
              欧美一区二区视频免费观看| 久久精品国产亚洲a| 一区二区三区四区五区精品| 亚洲一区国产视频| 免费成人黄色av| 亚洲伦理网站| 欧美在线观看网站| 欧美日韩在线高清| 亚洲第一精品福利| 欧美综合国产| 一区二区av在线| 免费在线日韩av| 国产亚洲欧洲一区高清在线观看| 亚洲国产日韩一级| 久久久久国产一区二区三区四区| 亚洲免费电影在线| 欧美成人中文| 在线观看精品视频| 久久国产免费| 六月天综合网| 在线观看视频一区二区| 亚洲精品国产精品国自产观看浪潮 | 欧美大尺度在线| 亚洲一级一区| 欧美日韩一区二区国产| 午夜欧美精品| 亚洲图中文字幕| 在线播放亚洲| 一区二区激情小说| 国内免费精品永久在线视频| 午夜精品久久久久久| 久久久美女艺术照精彩视频福利播放| 国产精品久久久久久久久久三级| 在线亚洲欧美视频| 欧美在线视频一区| 99精品久久久| 一本久道久久综合婷婷鲸鱼| 欧美黄色aa电影| 亚洲乱码国产乱码精品精可以看| 亚洲国产91| 欧美久久电影| 亚洲一区尤物| 欧美成人精精品一区二区频| 久久国产黑丝| 国产精品video| 欧美中文字幕在线播放| 欧美精品三级日韩久久| 亚洲一区二区三区四区视频| 亚洲一区二区三区在线| 国产一区亚洲一区| 一本色道**综合亚洲精品蜜桃冫| 亚洲黄色成人久久久| 欧美专区在线观看| 欧美亚洲在线| 久久综合久久久久88| 亚洲老司机av| 美玉足脚交一区二区三区图片| 久久xxxx精品视频| 国产精品一区久久久| 久久综合中文| 欧美精品 国产精品| 另类激情亚洲| 一区二区在线观看视频在线观看| 欧美国产视频日韩| 国产精品久久久久国产a级| 久久久一区二区三区| 嫩草伊人久久精品少妇av杨幂| 久久精品国产亚洲精品| 国产精品综合网站| 亚洲一区免费视频| 午夜亚洲伦理| 国产亚洲激情| 久久精品成人一区二区三区蜜臀| 日韩亚洲欧美成人一区| 欧美理论电影网| 亚洲视频精选在线| 亚洲国语精品自产拍在线观看| 久久久久久国产精品mv| 免费高清在线视频一区·| 永久久久久久| 欧美国产精品人人做人人爱| 亚洲激情影院| 亚洲高清成人| 欧美一区国产一区| 免费不卡在线视频| 日韩亚洲精品电影| 国产精品va| 欧美一区91| 欧美黄色成人网| 亚洲视频一区在线| 欧美岛国在线观看| 日韩一区二区久久| 久久er精品视频| 亚洲国产福利在线| 欧美日韩影院| 欧美在线视频一区二区三区| 欧美电影电视剧在线观看| 一本色道久久88综合亚洲精品ⅰ| 久久精品日韩欧美| 欧美专区18| 亚洲国产日韩欧美在线动漫| 欧美午夜电影网| 久久久久久噜噜噜久久久精品| 免费在线日韩av| 红杏aⅴ成人免费视频| 午夜免费日韩视频| 欧美一区二区黄| 国产伦精品一区二区三| 久久综合影音| 亚洲婷婷国产精品电影人久久 | 国产一区二区三区久久久久久久久| 久久青草欧美一区二区三区| 久久久久天天天天| 一本久道久久综合中文字幕| 国内成人在线| 国产精品电影观看| 欧美激情国产日韩| 久久久国产一区二区| 9国产精品视频| 亚洲大胆美女视频| 99re热精品| 在线播放一区| 国内久久视频| 国产精品视频自拍| 久久国产一区| 亚洲综合不卡| 欧美插天视频在线播放| 欧美亚洲日本网站| 亚洲视频在线看| 亚洲日本理论电影| 国产精品国产三级国产aⅴ浪潮| 美女福利精品视频| 久久婷婷国产综合精品青草| 亚洲自拍偷拍视频| 中文精品99久久国产香蕉| 亚洲精品影视| 亚洲人成在线观看一区二区| 欧美成人精品在线播放| 久久视频一区二区| 99这里只有久久精品视频| 在线国产日韩| 在线观看成人一级片| 一区二区三区在线免费视频| 国产一区二区三区在线观看免费视频 | 国产视频一区三区| 狂野欧美激情性xxxx| 欧美在线视频免费播放| 亚洲欧美日本精品| 亚洲大片在线| 欧美成人精品激情在线观看| 卡一卡二国产精品| 美女精品一区| 欧美激情精品| 亚洲韩国日本中文字幕| 久久久999精品视频| 久久精品视频在线观看| 久久精品在线观看| 久久久综合免费视频| 快she精品国产999| 亚洲成人自拍视频| 91久久综合| 一本一道久久综合狠狠老精东影业 | 久久久久久色| 免费人成精品欧美精品| 亚洲电影在线免费观看| 亚洲欧洲一二三| 一区二区三区欧美亚洲| 亚洲欧美日韩系列| 久久天堂精品| 欧美日韩精品福利| 老色鬼久久亚洲一区二区| 免费成人美女女| 欧美日韩综合一区| 欧美激情精品久久久久久大尺度 | 亚洲精选中文字幕| 亚洲午夜精品久久| 久久深夜福利免费观看| 欧美激情一区二区三区高清视频| 欧美日韩美女在线| 国产一区二区日韩| 亚洲麻豆一区| 久久成人精品无人区| 欧美激情91| 香蕉久久夜色精品国产| 欧美大尺度在线| 国产精品永久| 日韩视频一区二区在线观看 | 久久综合九色99| 亚洲精品视频二区| 久久久精彩视频| 国产精品久久久久高潮| 亚洲黄页一区| 久久精品中文字幕免费mv| 亚洲精品三级| 久久成人亚洲| 国产精品美女www爽爽爽视频| 亚洲国产专区| 老**午夜毛片一区二区三区| 一本色道久久综合亚洲精品不卡 | 日韩一区二区福利|