@@ -195,7 +195,7 @@ def debug_assembly(asm, gdbscript=None, vma=None, api=False):
195
195
196
196
>>> assembly = shellcraft.echo("Hello world!\n")
197
197
>>> io = gdb.debug_assembly(assembly)
198
- >>> io.recvline()
198
+ >>> io.recvline(timeout=1 )
199
199
b'Hello world!\n'
200
200
"""
201
201
tmp_elf = make_elf_from_assembly (asm , vma = vma , extract = False )
@@ -230,7 +230,7 @@ def debug_shellcode(data, gdbscript=None, vma=None, api=False):
230
230
>>> assembly = shellcraft.echo("Hello world!\n")
231
231
>>> shellcode = asm(assembly)
232
232
>>> io = gdb.debug_shellcode(shellcode)
233
- >>> io.recvline()
233
+ >>> io.recvline(timeout=1 )
234
234
b'Hello world!\n'
235
235
"""
236
236
if isinstance (data , six .text_type ):
@@ -283,7 +283,7 @@ def _execve_script(argv, executable, env, ssh):
283
283
return tmp .name
284
284
285
285
286
- def _gdbserver_args (pid = None , path = None , args = None , which = None , env = None , python_wrapper_script = None ):
286
+ def _gdbserver_args (pid = None , path = None , port = 0 , gdbserver_args = None , args = None , which = None , env = None , python_wrapper_script = None ):
287
287
"""_gdbserver_args(pid=None, path=None, args=None, which=None, env=None) -> list
288
288
289
289
Sets up a listening gdbserver, to either connect to the specified
@@ -292,6 +292,8 @@ def _gdbserver_args(pid=None, path=None, args=None, which=None, env=None, python
292
292
Arguments:
293
293
pid(int): Process ID to attach to
294
294
path(str): Process to launch
295
+ port(int): Port to use for gdbserver, default: random
296
+ gdbserver_args(list): List of additional arguments to pass to gdbserver
295
297
args(list): List of arguments to provide on the debugger command line
296
298
which(callaable): Function to find the path of a binary.
297
299
env(dict): Environment variables to pass to the program
@@ -300,6 +302,11 @@ def _gdbserver_args(pid=None, path=None, args=None, which=None, env=None, python
300
302
Returns:
301
303
A list of arguments to invoke gdbserver.
302
304
"""
305
+ if gdbserver_args is None :
306
+ gdbserver_args = list ()
307
+ elif not isinstance (gdbserver_args , (list , tuple )):
308
+ gdbserver_args = [gdbserver_args ]
309
+
303
310
if [pid , path , args ].count (None ) != 2 :
304
311
log .error ("Must specify exactly one of pid, path, or args" )
305
312
@@ -323,7 +330,7 @@ def _gdbserver_args(pid=None, path=None, args=None, which=None, env=None, python
323
330
324
331
orig_args = args
325
332
326
- gdbserver_args = [gdbserver , '--multi' ]
333
+ gdbserver_args = [gdbserver , '--multi' ] + gdbserver_args
327
334
if context .aslr :
328
335
gdbserver_args += ['--no-disable-randomization' ]
329
336
else :
@@ -351,7 +358,7 @@ def _gdbserver_args(pid=None, path=None, args=None, which=None, env=None, python
351
358
else :
352
359
gdbserver_args += ['--no-startup-with-shell' ]
353
360
354
- gdbserver_args += ['localhost:0' ]
361
+ gdbserver_args += ['localhost:%d' % port ]
355
362
gdbserver_args += args
356
363
357
364
return gdbserver_args
@@ -416,17 +423,20 @@ def _get_runner(ssh=None):
416
423
else : return tubes .process .process
417
424
418
425
@LocalContext
419
- def debug (args , gdbscript = None , exe = None , ssh = None , env = None , sysroot = None , api = False , ** kwargs ):
426
+ def debug (args , gdbscript = None , gdb_args = None , exe = None , ssh = None , env = None , port = 0 , gdbserver_args = None , sysroot = None , api = False , ** kwargs ):
420
427
r"""
421
428
Launch a GDB server with the specified command line,
422
429
and launches GDB to attach to it.
423
430
424
431
Arguments:
425
432
args(list): Arguments to the process, similar to :class:`.process`.
426
433
gdbscript(str): GDB script to run.
434
+ gdb_args(list): List of additional arguments to pass to GDB.
427
435
exe(str): Path to the executable on disk
428
436
env(dict): Environment to start the binary in
429
437
ssh(:class:`.ssh`): Remote ssh session to use to launch the process.
438
+ port(int): Gdb port to use, default: random
439
+ gdbserver_args(list): List of additional arguments to pass to gdbserver
430
440
sysroot(str): Set an alternate system root. The system root is used to
431
441
load absolute shared library symbol files. This is useful to instruct
432
442
gdb to load a local version of binaries/libraries instead of downloading
@@ -480,12 +490,12 @@ def debug(args, gdbscript=None, exe=None, ssh=None, env=None, sysroot=None, api=
480
490
Send a command to Bash
481
491
482
492
>>> io.sendline(b"echo hello")
483
- >>> io.recvline()
493
+ >>> io.recvline(timeout=30 )
484
494
b'hello\n'
485
495
486
496
Interact with the process
487
497
488
- >>> io.interactive() # doctest: +SKIP
498
+ >>> io.interactive(timeout=1 ) # doctest: +SKIP
489
499
>>> io.close()
490
500
491
501
Create a new process, and stop it at '_start'
@@ -504,7 +514,7 @@ def debug(args, gdbscript=None, exe=None, ssh=None, env=None, sysroot=None, api=
504
514
Send a command to Bash
505
515
506
516
>>> io.sendline(b"echo hello")
507
- >>> io.recvline()
517
+ >>> io.recvline(timeout=10 )
508
518
b'hello\n'
509
519
510
520
Interact with the process
@@ -516,53 +526,24 @@ def debug(args, gdbscript=None, exe=None, ssh=None, env=None, sysroot=None, api=
516
526
517
527
>>> io = gdb.debug(args=[b'\xde\xad\xbe\xef'], gdbscript='continue', exe="/bin/sh")
518
528
>>> io.sendline(b"echo $0")
519
- >>> io.recvline()
529
+ >>> io.recvline(timeout=10 )
520
530
b'\xde\xad\xbe\xef\n'
521
531
>>> io.close()
522
532
523
533
Demonstrate that LD_PRELOAD is respected
524
534
525
535
>>> io = process(["grep", "libc.so.6", "/proc/self/maps"])
526
- >>> real_libc_path = io.recvline().split()[-1]
536
+ >>> real_libc_path = io.recvline(timeout=1 ).split()[-1]
527
537
>>> io.close()
528
538
>>> import shutil
529
539
>>> local_path = shutil.copy(real_libc_path, "./local-libc.so") # make a copy of libc to demonstrate that it is loaded
530
540
>>> io = gdb.debug(["grep", "local-libc.so", "/proc/self/maps"], gdbscript="continue", env={"LD_PRELOAD": "./local-libc.so"})
531
- >>> io.recvline().split()[-1] # doctest: +ELLIPSIS
541
+ >>> io.recvline(timeout=1 ).split()[-1] # doctest: +ELLIPSIS
532
542
b'.../local-libc.so'
533
543
>>> io.close()
534
544
>>> os.remove("./local-libc.so") # cleanup
535
545
536
546
537
- Using GDB Python API:
538
-
539
- .. doctest::
540
- :skipif: is_python2
541
-
542
- Debug a new process
543
-
544
- >>> io = gdb.debug(['echo', 'foo'], api=True)
545
-
546
- Stop at 'write'
547
-
548
- >>> bp = io.gdb.Breakpoint('write', temporary=True)
549
- >>> io.gdb.continue_and_wait()
550
-
551
- Dump 'count'
552
-
553
- >>> count = io.gdb.parse_and_eval('$rdx')
554
- >>> long = io.gdb.lookup_type('long')
555
- >>> int(count.cast(long))
556
- 4
557
-
558
- Resume the program
559
-
560
- >>> io.gdb.continue_nowait()
561
- >>> io.recvline()
562
- b'foo\n'
563
- >>> io.close()
564
-
565
-
566
547
Using SSH:
567
548
568
549
You can use :func:`debug` to spawn new processes on remote machines as well,
@@ -591,17 +572,63 @@ def debug(args, gdbscript=None, exe=None, ssh=None, env=None, sysroot=None, api=
591
572
592
573
>>> io = gdb.debug(args=[b'\xde\xad\xbe\xef'], gdbscript='continue', exe="/bin/sh", ssh=shell)
593
574
>>> io.sendline(b"echo $0")
594
- >>> io.recvline()
575
+ >>> io.recvline(timeout=10 )
595
576
b'$ \xde\xad\xbe\xef\n'
596
577
>>> io.close()
597
578
598
579
Using an empty args[0] on a remote process
599
580
600
581
>>> io = gdb.debug(args=[], gdbscript='continue', exe="/bin/sh", ssh=shell)
601
582
>>> io.sendline(b"echo $0")
602
- >>> io.recvline()
583
+ >>> io.recvline(timeout=10 )
603
584
b'$ \n'
604
585
>>> io.close()
586
+
587
+
588
+ Using GDB Python API:
589
+
590
+ .. doctest::
591
+ :skipif: is_python2
592
+
593
+ Debug a new process
594
+
595
+ >>> io = gdb.debug(['echo', 'foo'], api=True)
596
+
597
+ or using ssh
598
+
599
+ >>> shell = ssh('travis', 'example.pwnme', password='demopass')
600
+ >>> ssh_io = gdb.debug(['/bin/echo', 'foo'], ssh=shell, api=True)
601
+
602
+ Stop at 'write'
603
+
604
+ >>> bp = io.gdb.Breakpoint('write', temporary=True)
605
+ >>> io.gdb.continue_and_wait()
606
+ >>> ssh_bp = ssh_io.gdb.Breakpoint('write', temporary=True)
607
+ >>> ssh_io.gdb.continue_and_wait()
608
+
609
+ Dump 'count'
610
+
611
+ >>> count = io.gdb.parse_and_eval('$rdx')
612
+ >>> long = io.gdb.lookup_type('long')
613
+ >>> int(count.cast(long))
614
+ 4
615
+ >>> count = ssh_io.gdb.parse_and_eval('$rdx')
616
+ >>> long = ssh_io.gdb.lookup_type('long')
617
+ >>> int(count.cast(long))
618
+ 4
619
+
620
+ Resume the program
621
+
622
+ >>> io.gdb.continue_nowait()
623
+ >>> io.recvline(timeout=1)
624
+ b'foo\n'
625
+ >>> io.close()
626
+
627
+ >>> ssh_io.gdb.continue_nowait()
628
+ >>> ssh_io.recvline(timeout=1)
629
+ b'foo\n'
630
+ >>> ssh_io.close()
631
+ >>> shell.close()
605
632
"""
606
633
if isinstance (args , six .integer_types + (tubes .process .process , tubes .ssh .ssh_channel )):
607
634
log .error ("Use gdb.attach() to debug a running process" )
@@ -615,8 +642,8 @@ def debug(args, gdbscript=None, exe=None, ssh=None, env=None, sysroot=None, api=
615
642
which = _get_which (ssh )
616
643
gdbscript = gdbscript or ''
617
644
618
- if api and runner is not tubes .process .process :
619
- raise ValueError ('GDB Python API is supported only for local processes' )
645
+ if api and runner is not tubes .process .process and not ssh :
646
+ raise ValueError ('GDB Python API is supported only for local and ssh processes' )
620
647
621
648
args , env = misc .normalize_argv_env (args , env , log )
622
649
if env :
@@ -632,17 +659,17 @@ def debug(args, gdbscript=None, exe=None, ssh=None, env=None, sysroot=None, api=
632
659
633
660
if ssh or context .native or (context .os == 'android' ):
634
661
if len (args ) > 0 and which (packing ._decode (args [0 ])) == packing ._decode (exe ):
635
- args = _gdbserver_args (args = args , which = which , env = env )
662
+ args = _gdbserver_args (gdbserver_args = gdbserver_args , args = args , port = port , which = which , env = env )
636
663
637
664
else :
638
665
# GDBServer is limited in it's ability to manipulate argv[0]
639
666
# but can use the ``--wrapper`` option to execute commands and catches
640
667
# ``execve`` calls.
641
668
# Therefore, we use a wrapper script to execute the target binary
642
669
script = _execve_script (args , executable = exe , env = env , ssh = ssh )
643
- args = _gdbserver_args (args = args , which = which , env = env , python_wrapper_script = script )
670
+ args = _gdbserver_args (gdbserver_args = gdbserver_args , args = args , port = port , which = which , env = env , python_wrapper_script = script )
644
671
else :
645
- qemu_port = random .randint (1024 , 65535 )
672
+ qemu_port = port if port != 0 else random .randint (1024 , 65535 )
646
673
qemu_user = qemu .user_path ()
647
674
sysroot = sysroot or qemu .ld_prefix (env = env )
648
675
if not qemu_user :
@@ -671,17 +698,19 @@ def debug(args, gdbscript=None, exe=None, ssh=None, env=None, sysroot=None, api=
671
698
# Set the .executable on the process object.
672
699
gdbserver .executable = exe
673
700
674
- # Find what port we need to connect to
675
701
if ssh or context .native or (context .os == 'android' ):
676
- port = _gdbserver_port (gdbserver , ssh )
702
+ gdb_port = _gdbserver_port (gdbserver , ssh )
703
+ if port != 0 and port != gdb_port :
704
+ log .error ("gdbserver port (%d) doesn't equals set port (%d)" % (gdb_port , port ))
705
+ port = gdb_port
677
706
else :
678
707
port = qemu_port
679
708
680
709
host = '127.0.0.1'
681
710
if not ssh and context .os == 'android' :
682
711
host = context .adb_host
683
712
684
- tmp = attach ((host , port ), exe = exe , gdbscript = gdbscript , ssh = ssh , sysroot = sysroot , api = api )
713
+ tmp = attach ((host , port ), exe = exe , gdbscript = gdbscript , gdb_args = gdb_args , ssh = ssh , sysroot = sysroot , api = api )
685
714
if api :
686
715
_ , gdb = tmp
687
716
gdbserver .gdb = gdb
@@ -948,7 +977,7 @@ def attach(target, gdbscript = '', exe = None, gdb_args = None, ssh = None, sysr
948
977
... detach
949
978
... quit
950
979
... ''')
951
- >>> io.recvline()
980
+ >>> io.recvline(timeout=10 )
952
981
b'Hello from process debugger!\n'
953
982
>>> io.sendline(b'echo Hello from bash && exit')
954
983
>>> io.recvall()
@@ -975,7 +1004,7 @@ def attach(target, gdbscript = '', exe = None, gdb_args = None, ssh = None, sysr
975
1004
976
1005
Observe the forced line
977
1006
978
- >>> io.recvline()
1007
+ >>> io.recvline(timeout=1 )
979
1008
b'Hello from process debugger!\n'
980
1009
981
1010
Interact with the program in a regular way
@@ -999,7 +1028,7 @@ def attach(target, gdbscript = '', exe = None, gdb_args = None, ssh = None, sysr
999
1028
... detach
1000
1029
... quit
1001
1030
... ''')
1002
- >>> io.recvline()
1031
+ >>> io.recvline(timeout=10 )
1003
1032
b'Hello from remote debugger!\n'
1004
1033
>>> io.sendline(b'echo Hello from bash && exit')
1005
1034
>>> io.recvall()
@@ -1018,7 +1047,7 @@ def attach(target, gdbscript = '', exe = None, gdb_args = None, ssh = None, sysr
1018
1047
>>> io.recvline(timeout=5) # doctest: +SKIP
1019
1048
b'Hello from ssh debugger!\n'
1020
1049
>>> io.sendline(b'This will be echoed back')
1021
- >>> io.recvline()
1050
+ >>> io.recvline(timeout=1 )
1022
1051
b'This will be echoed back\n'
1023
1052
>>> io.close()
1024
1053
"""
0 commit comments