调试器工作原理(1):基础篇

原著原作 : 

本文是一连串探索调试器任务规律的文字的第一篇。我缺乏自信这篇文字遏制了标号文字和运动的。,但据我看来从初步开端。。

涉及本文

我计划在这篇文字中绍介涉及Linux下的调试器进行的次要组成学派——成绩体系叫来。本文经过努力到达某事物目的行为准则是在一体32位Ubuntu体系上发达的。。请小心,这边的行为准则与平台亲密中间定位。,不管怎样进展到剩余学派平台上不太困难的。。

动机

理解我们的要做什么。,试设想象一下调试器是以任何的方式任务的。调试器可以启动少许加工,当时的调试它。,或许将本人关系到眼前的的加工。。它可以逐渐地地运转行为准则。,设置断点并运转顺序。,反省变量的值并下列叫来堆栈。。诸多调试器曾经欺骗了必然的年长的特点,譬如,在海报中使生效表示并叫来有或起作用。,您甚至可以直线修正做事方式的行为准则并视察修正。。

虽然近代的调试器都是复杂的大规模的顺序,但使成为一体惊讶的的是排列调试器的根底确是一概如此的复杂。调试器只用到了专有的由运转体系随着编制程序/关系器抚养的根底服务器,剩的刚才培养成绩。。(看见维基百科对此条宾格解说。),作者具有讽刺话意味。

Linux下的调试

Linux下调试器欺骗一体瑞士军刀般的器,这是pTrace体系叫来。。这是一体效能很多、效能构成复杂的器。,容许一体加工把持另一体加工的运转。,并且可以被监控和漏到这么样做事方式中。。成绩亲自必要一本适度的一节的书才干对其举行完好的解说,这执意为什么我只计划经过容器把关键点放在它的现实企图上。让我们的持续深化讨论。。

遍历做事方式的行为准则

我要写一体鄙人列铅字下运转的做事方式的容器。,这边我们的要单步遍历这么样加工的行为准则——由CPU所使生效的机器代码(缀编直接地性的)。我会给你一体容器行为准则在这边。,解说每个学派,在本文的末了,您可以下载完好的C顺序寄给报社。,你可以本人编制和使生效。。楼塔设计,我们的必需品编制一体顺序。,它生利一体子做事方式来使生效用户称呼委任的命令。,父加工下列此子加工。。率先,次要效能是这样的事物的。:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

intmain(intargc,char**argv)

{

    pid_t child_pid;

    if(argc<2){

        fprintf(stderr,“Expected a program name as argument\n”);

        return1;

    }

    child_pid=fork();

    if(child_pid==0)

        run_target(argv[1]);

    elseif(child_pid>0)

        run_debugger(child_pid);

    else{

        perror(“fork”);

        return1;

    }

    return0;

}

行为准则相当复杂。,我们的经过叉子生利一体新的子做事方式。。后续if声明块处置子加工(这边称为TAR)。,而else if声明块处置父加工(这边称为“调试器”)。以下是目的做事方式。:

1

2

3

4

5

6

7

8

9

10

11

12

13

voidrun_target(constchar*programname)

{

    procmsg(“target
动身。 will run ”%s”\n”
,programname);

    /*
Allow tracing of this process */

    if(成绩(PTRACE_TRACEME,0,0,0)<0){

        perror(“成绩”);

        return;

    }

    /*
Replace this 做事方式的 image with the given program */

    execl(programname,programname,0);

}

这学派最有意思的关心在成绩叫来。成绩的典型是(在sys/):

1

long成绩(enum__成绩_request request,  pid_t pid,void*addr,  void*data);

第一体参量是盘问。,它可以是一体明细表义的常数值,从p循迹开端。。瞬间个参量称呼委任加工ID。,第三和第四音级参量是地址和定向datum的复数的帮助。,用于运转内存。。下面行为准则段经过努力到达某事物目的成绩叫来运用了PTRACE_TRACEME盘问,这表白这么样子做事方式必要运转体系内核。这么样问在手册中解说得很变清澈。:

标示该加工由其父加工下列。。发送到这么样加工的任何的导火线(不计SIGKEY)大都会原因加工出错。,它的父加工将经过盼望通告。再者,该加工较晚地缠住对exec()的叫来都将使运转体系发作一体SIGTRAP导火线发发出信息它,这给父加工抚养了把持子加工B的时机。。免得您不缺少被父加工下列,这么样你不一定运用这么样盘问。。(PID)、addr、datum的复数被疏忽)

我集中注意力了这么样容器,我们的感兴趣。。小心,run_target在成绩叫来较晚地接着做的是经过execl来叫来我们的称呼委任的顺序。这将被解说为我们的杰出的学派的一学派。,运转体系的内核在子加工在前方中止加工。,并向父加工发送一体导火线。。

这么样,是时辰看一眼父加工必要做什么了。:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

voidrun_debugger(pid_t
child_pid)

{

    intwait_status;

    unsignedicounter=0;

    procmsg(“debugger
started\n”
);

    /*
Wait for child to stop on its first instruction */

    wait(&wait_status);

    while(WIFSTOPPED(wait_status)){

        icounter++;

        /*
Make the child execute another instruction */

        if(成绩(PTRACE_SINGLESTEP,child_pid,0,0)<0){

            perror(“成绩”);

            return;

        }

        /*
Wait for child to stop on its next instruction */

        wait(&wait_status);

    }

    procmsg(“the
child executed %u instructions\n”
,icounter);

}

经过下面的行为准则我们的可以回忆一下,子加工开端使生效EXEC叫来后,它将中止并收执SIGRAT导火线。。父加工正盼望经过首次WEW发作此事实。。一旦子加工中止,免得子加工中止运转,由于,WiFrue重提true),父加工反省事实。。

父加工接下来将做是什么这么样AR最风趣的学派。。父加工经过PTRACE_SINGLESTEP随着子加工的id号来叫来成绩。这么样做是通知运转体系——请重行发起人加工,不管怎样当子加工使生效下一体直接地性的时,它中止。。当时的父加工盼望该子再次中止。,统统成圈持续使生效。。当您从盼望中记下导火线时,它缺点涉及中止小孩PRO的。,成圈完毕。在这么样下列顺序的合格的运转做事方式中,它将记下子加工合格的放弃斗争的导火线(WIFFEXIT)。

icounter会总额子加工使生效的直接地性的数字。这么样我们的这么样复杂的容器现实上黑金色、黑色做了点效用的事实——经过在命令行上称呼委任一体顺序名,我们的的示例将使生效称呼委任的顺序。,当时的,顺序从B使生效的CPU直接地性的总额。。让我们的来看一眼现实运转。。

现实结帐

我编制了以下复杂的顺序。,当时的在我们的的下列顺序下使生效。:

#include

intmain()

{

    printf(Hello,world!\n);

    return0;

}

令我大吃一惊的是,,我们的的下列顺序运转了很长的工夫当时的表明显示一公共用地超越100000条直接地性的记下了使生效。这刚才一体复杂的蜡纸油印机盘问。,为什么会这样的事物?答案很风趣。。默许影响下,Linux经过努力到达某事物目的gcc编制程序会定态关系到C运转时库。这暗示任何的顺序运转的第一件事执意工作量定态。。这必要丰盛的的行为准则进行——记着,我们的的复杂下列顺序将对使生效的每个直接地性的举行计数。,这不仅仅是次要的效能。,但统统做事方式。。

这么样,当我采取-static注意定态关系这么样结帐顺序时(小心到可使生效寄给报社这么样累积而成了500KB的显得庞大,由于它定态衔接C运转库。,我们的的下列顺序表明显示独自的7000条摆布的直接地性的被使生效了。这依然是很多。,不管怎样免得您认识到LIBC的设定初值依然在EXE在前方,洗涤运转将在主控后举行。,嗯,这是合乎情理的。。并且,Primtf也一体复有或起作用。。

我们的对此仍不满的。,我缺少能便笺必然的东西。,譬如,我可以从完全上便笺每个直接地性的必要使生效什么。。这可以经过缀编行为准则进行。。因而我把这么样高强度。,躲进地洞顺序缀编(GCC)) s)是下沉。:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

section    .text

    ;The
_start
symbol
must
be
declared
forthe
linker(ld)

    global_start

_start:

    ;Prepare
arguments
forthe
sys_write
system
call:

    ;  
eax:system
call
number(sys_write)

    ;  
ebx:file
descriptor(stdout)

    ;  
ecx:pointer
tostring

    ;  
edx:stringlength

    mov    edx,len

    mov    ecx,msg

    mov    ebx,1

    mov    eax,4

    ;Execute
the
sys_write
system
call

    int    0x80

    ;Execute
sys_exit

    mov    eax,1

    int    0x80

section  
.data

msg
db    高强度,
world!”
,0xa

len
equ    $msg

这就十足了。如今下列顺序将表明曾经使生效了7条直接地性的。,我可以容易地地从缀编行为准则中批准这点。。

深远的直接地性的流

缀编码顺序足以让我为各种的绍介成绩的另一体弱小的效能——详尽的反省被下列加工的情势。这边是Run-Unjiggor有或起作用的另一体版本。:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

voidrun_debugger(pid_t child_pid)

{

    intwait_status;

    unsignedicounter=0;

    procmsg(“debugger started\n”);

    /* Wait for child to stop on its first instruction */

    wait(&wait_status);

    while(WIFSTOPPED(wait_status)){

        icounter++;

        structuser_regs_struct regs;

        成绩(PTRACE_GETREGS,child_pid,0,®s);

        unsignedinstr=成绩(PTRACE_PEEKTEXT,child_pid,regs.eip,0);

        procmsg(“icounter = 美国一批备用药品 = 0x%08x.  instr = 0x%08x\n”,

                    icounter,regs.eip,instr);

        /* Make the child execute another instruction */

        if(成绩(PTRACE_SINGLESTEP,child_pid,0,0)<0){

            perror(“成绩”);

            return;

        }

        /* Wait for child to stop on its next instruction */

        wait(&wait_status);

    }

    procmsg(“the child executed %u instructions\n”,icounter);

}

与前一版本相形,专有的的分别是while成圈的开端。。这边有两个新的成绩叫来。第一体读取加工的表达值到一体机构体中。体系中清晰度了用户机构。这是一体风趣的关心,免得你翻开这么样头寄给报社。,在寄给报社顶部邻近的有这样的事物的正文。:

1

/* 本寄给报社的专有的宾格是GDB。,并且只一致的GDB。。到某种状态这份寄给报社,不要看过度。。除非GDB,不然不要运用任何的剩余学派企图。,除非你发生本人在做什么。。*/

如今,我不发生你在想什么。,但我觉得我们的是在完完全全地的轨道上。。无论以任何的方式,回到我们的的容器。。一旦我们的利润缠住表达值,,我们的就可以经过PTRACE_PEEKTEXT注意随着将(x86架构上的传播直接地性的帮助)做参量传入成绩来叫来。我们的记下的是直接地。。让我们的在缀编行为准则上运转下列顺序的新发行。。

$simple_tracer traced_helloworld

[5700]debugger started

[5701]target started.will run”traced_helloworld”

[5700]icounter=1.  EIP=0x08048080.  instr=0x00000eba

[5700]icounter=2.  EIP=0x08048085.  instr=0x0490a0b9

[5700]icounter=3.  EIP=0x0804808a.  instr=0x000001bb

[5700]icounter=4.  EIP=0x0804808f.  instr=0x000004b8

[5700]icounter=5.  EIP=0x08048094.  instr=0x01b880cd

Hello,world!

[5700]icounter=6.  EIP=0x08048096.  instr=0x000001b8

[5700]icounter=7.  EIP=0x0804809b.  instr=0x000080cd

[5700]the childexecuted7instructions

OK,因而如今不计IcOnter。,我们的也可以在每个方式中便笺直接地性的和直接地性的。。我们的以任何的方式批准这种完完全全地性?我们的可以在可使生效FIL上使生效ObjDIP D进行:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

$objdumpdtraced_helloworld

traced_helloworld:    
file
formatelf32i386

Disassembly
of
section.text:

08048080<.text>:

8048080:    
ba0e000000          mov    $0xe,%edx

8048085:    
b9
a0900408          mov    $0x80490a0,%ecx

804808a:    
bb01000000          mov    $0x1,%ebx

804808f:    
b804000000          mov    $0x4,%eax

8048094:    
cd80                  
int    $0x80

8048096:    
b801000000          mov    $0x1,%eax

804809b:    
cd80                  
int    $0x80

运用这么样输入来构成我们的的下列顺序输入。,视察同一体关心一定容易地。。

与运转做事方式关系

你曾经发生了调试器也可以关系到曾经是运转情势的加工上。便笺这边,你不一定找到惊讶的。,这也经过成绩来进行的。这必要pTraceEnAsple盘问。。我不见得给你一体范本行为准则在这边。,由于我们的曾经看过行为准则了。,这一定容易地进行。。由于教书的宾格,这边的方式更实用的(由于我们的可以敏捷地中止孩子)。。

行为准则

本文给予的这么样复杂的下列顺序的完好行为准则(更年长的少许,你可以蜡纸油印机出详细的直接地性的)你可以在这边找到它们。。经过Wall的顺序
–pedantic STD=C99编制调动球员是在GCC版本上编制的。。

收场白和下一步要做的。

固然,本文并没有遏制过度的实质——我们的离一体真正使得的调试器还差的有多远。不管怎样,我缺少这篇文字曾经揭开了调试做事方式经过努力到达某事物目的秘密。。成绩是一体欺骗诸多效能的体系叫来,眼前,我们的只揭露了这些效能经过努力到达某事物目的专有的。。

能逐级使生效行为准则是效用的。,但结果对公众不完全开放的。。用高强度, 以躲进地洞为例,经过努力到达某事物次要效能,您必要经过数千条直接地性的来设定初值C运转时。。这缺点很实用的。。我们的祝愿的梦想receiver 收音机是在MAI登记设置断点。,从断点使生效单步使生效。鄙人一篇文字中,我将绍介以任何的方式进行断点机制。。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

`