【转】玩转ptrace(二)

浅谈ARM上的Ptrace /* author:少仲 blog: http://blog.youkuaiyun.com/py_panyu weibo: http://weibo.com/u/3849478598 欢迎转载,转载请注明出处. */ 0x0 前言 你想过怎么实现对系统调用的拦截吗?你尝试过通过改变系统调用的参数来愚弄你的系统kernel吗?你想过调试器是如何使运行中的进程暂停并且控制它吗?你可能会开 阅读详情

原文:http://blog.youkuaiyun.com/silentvoid/archive/2007/01/08/1477515.aspx

 

by Pradeep Padala

Created 2002-11-01 02:00

翻译 : Magic.D

 

在第一部分中我们已经看到 ptrace 怎么获取子进程的系统调用以及改变系统调用的参数。在这篇文章中,我们将要研究如何在子进程中设置断点和往运行中的程序里插入代码。实际上调试器就是用这种方法来设置断点和执行调试句柄。与前面一样,这里的所有代码都是针对 i386 平台的。

 

附着在进程上

 

在第一部分钟,我们使用 ptrace(PTRACE_TRACEME , …) 来跟踪一个子进程,如果你只是想要看进程是怎么进行系统调用和跟踪程序的,这个做法是不错的。但如果你要对运行中的进程进行调试,则需要使用   ptrace(  PTRACE_ATTACH , ….)

 

当   ptrace(  PTRACE_ATTACH , …) 在被调用的时候传入了子进程的 pid 时,   它大体是与 ptrace(  PTRACE_TRACEME , …) 的行为相同的,它会向子进程发送 SIGSTOP 信号,于是我们可以察看和修改子进程,然后使用   ptrace(  PTRACE_DETACH , …) 来使子进程继续运行下去。

 

下面是调试程序的一个简单例子

 

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

    
return   0 ;
}

 

将上面的代码保存为 dummy2.c 。按下面的方法编译运行:

gcc -o dummy2 dummy2.c

./dummy2 &


 

现在我们可以用下面的代码来附着到 dummy2 上。

 

#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>  " ,
               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  " , 
           regs.eip, ins);
    ptrace(PTRACE_DETACH, traced_process, 
           NULL, NULL);
    
return   0 ;
}

 

上面的程序仅仅是附着在子进程上,等待它结束,并测量它的 eip(  指令指针 ) 然后释放子进程。

 

设置断点

 

调试器是怎么设置断点的呢?通常是将当前将要执行的指令替换成 trap 指令,于是被调试的程序就会在这里停滞,这时调试器就可以察看被调试程序的信息了。被调试程序恢复运行以后调试器会把原指令再放回来。这里是一个例子:

 

#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] 
=   ' ' ;
}


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>  " , 
               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  " );
    printf(
" Press <enter> to continue  " );
    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 ;

}

 

上面的程序将把三个 byte 的内容进行替换以执行 trap 指令,等被调试进程停滞以后,我们把原指令再替换回来并把 eip 修改为原来的值。下面的图中演示了指令的执行过程

 

 

 

1.  进程停滞后

2.  替换入trap 指令

 

 

3. 断点成功,控制权交给了调试器

4.  继续运行,将原指令替换回来并将eip 复原

 

 

在了解了断点的机制以后,往运行中的程序里面添加指令也不再是难事了,下面的代码会使原程序多出一个 ”hello world” 的输出

 

这时一个简单的 ”hello world” 程序,当然为了我们的特殊需要作了点修改:

 

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" "
       );
}
 

使用   gcc –o hello hello.c 来编译它。

 

在 backward 和 forward 之间的跳转是为了使程序能够找到 ”hello world”  字符串的地址。

 

使用 GDB 我们可以得到上面那段程序的机器码。启动 GDB, 然后对程序进行反汇编:

 

(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.


 

 

我们需要使用从 man+3 到 backward+20 之间的字节码,总共 41 字节。使用 GDB 中的 x 命令来察看机器码。

 

(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

 

已经有了我们想要执行的指令,还等什么呢?只管把它们根前面那个例子一样插入到被调试程序中去!

 

代码:

 

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>  " , 
               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  " );
    putdata(traced_process, regs.eip, backup, len);
    ptrace(PTRACE_SETREGS, traced_process, 
           NULL, 
& regs);
    printf(
" Letting it continue with  "
           
" original flow  " );
    ptrace(PTRACE_DETACH, traced_process,
           NULL, NULL);
    
return   0 ;
}

将代码插入到自由空间

 

在前面的例子中我们将代码直接插入到了正在执行的指令流中,然而,调试器可能会被这种行为弄糊涂,所以我们决定把指令插入到进程中的自由空间中去。通过察看 /proc/pid/maps 可以知道这个进程中自由空间的分布。接下来这个函数可以找到这个内存映射的起始点:

 

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;
}

在 /proc/pid/maps 中的每一行都对应了进程中一段内存区域。主函数的代码如下:

 

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>  " , 
               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  " );
    putdata(traced_process, addr, backup, len);
    ptrace(PTRACE_SETREGS, traced_process, 
           NULL, 
& oldregs);
    printf(
" Letting it continue with  "
           
" original flow  " );
    ptrace(PTRACE_DETACH, traced_process, 
           NULL, NULL);
    
return   0 ;
}

ptrace 的幕后工作

那么,在使用 ptrace 的时候,内核里发生了声么呢?这里有一段简要的说明:

 

当一个进程调用了   ptrace(  PTRACE_TRACEME , …) 之后,内核为该进程设置了一个标记,注明该进程将被跟踪。内核中的相关原代码如下:

 

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;

}

 

一次系统调用完成之后,内核察看那个标记,然后执行 trace 系统调用(如果这个进程正处于被跟踪状态的话)。其汇编的细节可以在   arh/i386/kernel/entry.S 中找到。

 

现在让我们来看看这个 sys_trace() 函数(位于   arch/i386/kernel/ptrace.c  )。它停止子进程,然后发送一个信号给父进程,告诉它子进程已经停滞,这个信号会激活正处于等待状态的父进程,让父进程进行相关处理。父进程在完成相关操作以后就调用 ptrace(  PTRACE_CONT , …) 或者 ptrace(  PTRACE_SYSCALL , …),  这将唤醒子进程,内核此时所作的是调用一个叫 wake_up_process()  的进程调度函数。其他的一些系统架构可能会通过发送 SIGCHLD 给子进程来达到这个目的。

 

小结:

ptrace 函数可能会让人们觉得很奇特,因为它居然可以检测和修改一个运行中的程序。这种技术主要是在调试器和系统调用跟踪程序中使用。它使程序员可以在用户级别做更多有意思的事情。已经有过很多在用户级别下扩展操作系统得尝试,比如 UFO, 一个用户级别的文件系统扩展,它使用 ptrace 来实现一些安全机制。

 

作者 :

Pradeep Padala,

  p_padala@yahoo.com

http://www.cise.ufl.edu/~ppadala

玩转Android10源码开发定制(七)修改ptrace绕过反调试 一、安卓10中ptrace介绍 1.源码位置追踪 在Android10中,ptrace函数定义文件路径为: bionic/libc/include/sys/ptrace.h 函数原型定义如下: long ptrace(int __request, ...); ptrace函数实现文件路径为: bionic/libc/bionic/ptrace.cpp 函数实现代码如下: long ptrace(int ... 阅读详情

相关推荐

Ubuntu 22.04远程桌面终极指南:从零配置RDP到解决黑屏问题

本文提供了一份Ubuntu 22.04远程桌面配置的终极指南,详细讲解了如何从零开始安装和配置RDP服务,并重点解决了连接后常见的黑屏问题。通过分析会话冲突、修复启动脚本及配置权限策略,帮助用户实现稳定、高效的远程桌面连接,优化分辨率、禁用锁屏,打造无缝的远程工作体验。

sony5的博客 1381

Android平台arm64 ptrace hook bridge_code debug

参考文档: http://blog.chinaunix.net/uid-20361370-id-1962528.html 共享库注入–injectso实例 http://www.aiuxian.com/article/p-1295924.html Android平台的 Ptrace, 注入, Hook 全攻略 最近学习ptrace,也想尝试写个hook的程序玩玩。最后阶段的bridge_co

azfa123的博客 2446

ScanNet数据集详解

ScanNet 是一个 RGB-D 视频数据集,在超过 1500 次扫描中包含了 250 万个视图,并标注了 3D 相机位姿、表面重建和实例级的语义分割。为了收集这些数据,设计了一个易于使用且具有扩展性的 RGB-D 捕获系统,包括自动化的表面重建和众包的语义标注。如果您想下载 ScanNet 数据,请使用您的机构邮箱地址填写一份 ScanNet 使用条款协议,并发送至 scannet@googlegroups.com。压缩包是将3D的标注信息“拍扁”成2D图片的结果,对应原始视频的每一帧。

frostmelody 全网同名,大家多多关注呀~ 持续分享优质内容! 3916

玩转ptrace(二)

by Pradeep PadalaCreated 2002-11-01 02:00翻译: Magic.D 在第一部分中我们已经看到ptrace怎么获取子进程的系统调用以及改变系统调用的参数。在这篇文章中,我们将要研究如何在子进程中设置断点和往运行中的程序里插入代码。实际上调试器就是用这种方法来设置断点和执行调试句柄。与前面一样,这里的所有代码都是针对i386平台的。 附着在

4914

C++常见面试题总结

C++常见面试题总结

xu_fu_yong的专栏 8万+

ERPS协议原理

目录 1、ERPS概述: 2、ERPS名词概念解释: 3、ERPS协议报文: 4、ERPS单环基本原理: 5、单环多实例原理: 6、多环原理: 1、ERPS概述: ERPS(Ethernet Ring Protection Switching),即以太网多环保护技术,是ITU-T定义的一种二层破环协议标准,标准号为ITU-T G.8032/Y1344,因此又称为G.8032。它定义了RAPS(Ring Auto Protection Switching)协议报文和保护倒换机制。 ERPS目前支持v1和v2两

weixin_44880796的博客 1万+

Linux arm64异常简介和系统调用过程

Linux arm64异常简介和系统调用过程

小立仔 2106

手把手教你用Xilinx SelectIO IP核搞定ADS42LB69高速ADC的DDR数据采集(附仿真调试技巧)

本文详细介绍了如何使用Xilinx SelectIO IP核实现ADS42LB69高速ADC的DDR数据采集,包括IP核配置、仿真验证和硬件调试技巧。通过精准的时序控制和实战调试方法,解决高速数据采集中的时序对齐问题,提升系统稳定性和可靠性。特别适合FPGA工程师和高速ADC接口设计者参考。

weixin_42533622的博客 151

Ptrace调试ARM架构多线程

百度: ptrace()系统调用提供了一种方法可以使得追踪者(tracer)来对被追踪者(tracee)进行观察与控制。具体表现为可以检查tracee中内存以及寄存器的值。ptrace首要地被用于实现断点debug与系统调用追踪。 首先,tracee process必须要被tracer attach上(也就是我们启动gdb后的 attach pid),需要注意的是,attach和后续的命令是针对每个线程来说的。如果是一个多线程的程序,每个线程都要被单独的attach才行。这里主要强调了,tracee(

q759451733的博客 2069

AAC编解码器在实时通信中的性能对比与应用选择

本文深入分析了AAC编解码器在实时通信中的性能优化,重点对比了faac、faad2与fdk-aac三大开源方案在WebRTC场景下的表现。通过实测数据揭示其在延迟、音质和资源消耗等关键指标的差异,并提供场景化选型建议与调优技巧,帮助开发者提升实时音视频通信质量。

weixin_29210727的博客 388

AFSim插件开发实战:从环境搭建到功能扩展

本文详细介绍了AFSim插件开发的完整流程,从环境搭建到功能扩展,涵盖了SDK配置、插件创建、核心代码解析、功能扩展实战、调试优化以及高级开发技巧。通过实战案例和实用技巧,帮助开发者快速掌握AFSim插件开发,提升仿真系统的扩展性和灵活性。

weixin_29309475的博客 576

玩转ptrace(二) [转]

code:http://ftp.ssc.com/pub/lj/listings/issue104/6210.tgz 英文原文http://www.linuxjournal.com/article/6210-------------------------------------------------------------------------------------------------

scancsdn的专栏 422

出现 RuntimeError: adaptive_avg_pool2d_backward_cuda does not have a deterministic implementation的解决方法

大致就是在执行主函数的训练函数中,在反向传播这一块出现了问题。解决方法就是添加如下代码块。找到提示bug的行数。

码农研究僧的博客 5532

[转] Playing with ptrace, Part II — 玩转ptrace(二)

Playing with ptrace, Part IIby Pradeep Padala p_padala@yahoo.com http://www.cise.ufl.edu/~ppadalaCreated 2002-11-01 02:00 翻译: Magic.D E-mail: adamgic@163.com 在第一部分中我们已经看到ptrace怎么获取子进程的系统调用以及改变系统调用的参数。...

weixin_30814319的博客 315

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

玩转ptrace(一)

by Pradeep PadalaCreated 2002-11-01 02:00翻译: Magic.D E-mail: adamgic@163.com译者序:在开发Hust Online Judge的过程中,查阅了不少资料,关于调试器技术的资料在网上是很少,即便是UNIX编程巨著《UNIX环境高级编程》中,相关内容也不多,直到我在http://www.linuxjournal.c

5838

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

玩转ptrace

原文地址:http://www.cnblogs.com/catch/p/3476280.html点击打开链接 二点击打开链接 [译] 玩转ptrace (一) [本文翻译自这里: http://www.linuxjournal.com/article/6100?page=0,0,作者:Pradeep Padaia]   你是否曾经想过怎样才能拦截系统调用?你是否曾经想过通过修改一

yang_chen_shi_wo的博客 372

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

玩转ptrace(一) [转]

原文:http://blog.chinaunix.net/u/19651/showart_362901.html英文原文:http://www.linuxjournal.com/article/6100code:ftp.ssc.com/pub/lj/listings/issue103/6011.tgz-------------------------------------------------

scancsdn的专栏 568

玩转ptrace(转)

 http://www.kgdb.info/gdb/playing_with_ptrace_part_i/ Playing with ptrace, Part Iby Pradeep Padala p_padala@yahoo.com http://www.cise.ufl.edu/~ppadalaCreated 2002-11-01 02:00翻译: Magic.D E-mail

gracioushe的专栏 914

[译] 玩转ptrace (一) 拦截系统调用

[本文翻译自这里: http://www.linuxjournal.com/article/6100?page=0,0,作者:Pradeep Padaia]   你是否曾经想过怎样才能拦截系统调用?你是否曾经想过通过修改一下系统调用的参数来耍一把内核?你是否想过调试器是怎样把一个进程停下来,然后把控制权转移给你的?如果你以为这些都是通过复杂的内核编程来实现的,那你就错了,事实上,Linux 提

xiaoxinyu316的专栏 3444
上一篇: 【转】玩转ptrace(一)
下一篇: 【转】GTK编程——构件的显现、映射和显示
code09
博客等级 码龄18年 31粉丝 56原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值