@@ -389,7 +389,7 @@ class Corefile(ELF):
389
389
390
390
>>> core.vdso.data[:4]
391
391
'\x7fELF'
392
- >>> core.libc # docteset : +ELLIPSIS
392
+ >>> core.libc # doctest : +ELLIPSIS
393
393
Mapping('/lib/x86_64-linux-gnu/libc-...', ...)
394
394
395
395
The corefile also contains a :attr:`.Corefile.stack` property, which gives
@@ -421,14 +421,54 @@ class Corefile(ELF):
421
421
422
422
>>> s = ssh('travis', 'example.pwnme')
423
423
>>> _ = s.set_working_directory()
424
- >>> elf = ELF.from_assembly('int3' )
424
+ >>> elf = ELF.from_assembly(shellcraft.trap() )
425
425
>>> path = s.upload(elf.path)
426
426
>>> _ =s.chmod('+x', path)
427
427
>>> io = s.process(path)
428
428
>>> io.wait()
429
429
-1
430
430
>>> io.corefile.signal == signal.SIGTRAP # doctest: +SKIP
431
431
True
432
+
433
+ Tests:
434
+
435
+ These are extra tests not meant to serve as examples.
436
+
437
+ Corefile.getenv() works correctly, even if the environment variable's
438
+ value contains embedded '='. Corefile is able to find the stack, even
439
+ if the stack pointer doesn't point at the stack.
440
+
441
+ >>> elf = ELF.from_assembly(shellcraft.crash())
442
+ >>> io = elf.process(env={'FOO': 'BAR=BAZ'})
443
+ >>> io.wait()
444
+ >>> core = io.corefile
445
+ >>> core.getenv('FOO')
446
+ 'BAR=BAZ'
447
+ >>> core.sp == 0
448
+ True
449
+ >>> core.sp in core.stack
450
+ False
451
+
452
+ Corefile gracefully handles the stack being filled with garbage, including
453
+ argc / argv / envp being overwritten.
454
+
455
+ >>> context.clear(arch='i386')
456
+ >>> assembly = '''
457
+ ... LOOP:
458
+ ... mov dword ptr [esp], 0x41414141
459
+ ... pop eax
460
+ ... jmp LOOP
461
+ ... '''
462
+ >>> elf = ELF.from_assembly(assembly)
463
+ >>> io = elf.process()
464
+ >>> io.wait()
465
+ >>> core = io.corefile
466
+ >>> core.argc, core.argv, core.env
467
+ (0, [], {})
468
+ >>> core.stack.data.endswith('AAAA')
469
+ True
470
+ >>> core.fault_addr == core.sp
471
+ True
432
472
"""
433
473
434
474
_fill_gaps = False
@@ -454,6 +494,9 @@ def __init__(self, *a, **kw):
454
494
#: variable.
455
495
#:
456
496
#: Note: Use with the :meth:`.ELF.string` method to extract them.
497
+ #:
498
+ #: Note: If FOO=BAR is in the environment, self.env['FOO'] is the
499
+ #: address of the string "BAR\x00".
457
500
self .env = {}
458
501
459
502
#: :class:`list`: List of addresses of arguments on the stack.
@@ -664,7 +707,22 @@ def ppid(self):
664
707
665
708
@property
666
709
def signal (self ):
667
- """:class:`int`: Signal which caused the core to be dumped."""
710
+ """:class:`int`: Signal which caused the core to be dumped.
711
+
712
+ Example:
713
+
714
+ >>> elf = ELF.from_assembly(shellcraft.trap())
715
+ >>> io = elf.process()
716
+ >>> io.wait()
717
+ >>> io.corefile.signal == signal.SIGTRAP
718
+ True
719
+
720
+ >>> elf = ELF.from_assembly(shellcraft.crash())
721
+ >>> io = elf.process()
722
+ >>> io.wait()
723
+ >>> io.corefile.signal == signal.SIGSEGV
724
+ True
725
+ """
668
726
if self .siginfo :
669
727
return int (self .siginfo .si_signo )
670
728
if self .prstatus :
@@ -675,7 +733,17 @@ def fault_addr(self):
675
733
""":class:`int`: Address which generated the fault, for the signals
676
734
SIGILL, SIGFPE, SIGSEGV, SIGBUS. This is only available in native
677
735
core dumps created by the kernel. If the information is unavailable,
678
- this returns the address of the instruction pointer."""
736
+ this returns the address of the instruction pointer.
737
+
738
+
739
+ Example:
740
+
741
+ >>> elf = ELF.from_assembly('mov eax, 0xdeadbeef; jmp eax', arch='i386')
742
+ >>> io = elf.process()
743
+ >>> io.wait()
744
+ >>> io.corefile.fault_addr == io.corefile.eax == 0xdeadbeef
745
+ True
746
+ """
679
747
if self .siginfo :
680
748
return int (self .siginfo .sigfault_addr )
681
749
@@ -848,11 +916,31 @@ def _parse_stack(self):
848
916
# Now we can fill in the environment
849
917
env_pointer_data = stack [start_of_envp :p_last_env_addr + self .bytes ]
850
918
for pointer in unpack_many (env_pointer_data ):
851
- end = stack .find ('=' , last_env_addr )
852
919
853
- if pointer in stack and end in stack :
854
- name = stack [pointer :end ]
855
- self .env [name ] = pointer
920
+ # If the stack is corrupted, the pointer will be outside of
921
+ # the stack.
922
+ if pointer not in stack :
923
+ continue
924
+
925
+ try :
926
+ name_value = self .string (pointer )
927
+ except Exception :
928
+ continue
929
+
930
+ name , value = name_value .split ('=' , 1 )
931
+
932
+ # "end" points at the byte after the null terminator
933
+ end = pointer + len (name_value ) + 1
934
+
935
+ # Do not mark things as environment variables if they point
936
+ # outside of the stack itself, or we had to cross into a different
937
+ # mapping (after the stack) to read it.
938
+ # This may occur when the entire stack is filled with non-NUL bytes,
939
+ # and we NULL-terminate on a read failure in .string().
940
+ if end not in stack :
941
+ continue
942
+
943
+ self .env [name ] = pointer + len (name ) + len ('=' )
856
944
857
945
# May as well grab the arguments off the stack as well.
858
946
# argc comes immediately before argv[0] on the stack, but
@@ -904,15 +992,32 @@ def getenv(self, name):
904
992
905
993
Returns:
906
994
:class:`str`: The contents of the environment variable.
995
+
996
+ Example:
997
+
998
+ >>> elf = ELF.from_assembly(shellcraft.trap())
999
+ >>> io = elf.process(env={'GREETING': 'Hello!'})
1000
+ >>> io.wait()
1001
+ >>> io.corefile.getenv('GREETING')
1002
+ 'Hello!'
907
1003
"""
908
1004
if name not in self .env :
909
1005
log .error ("Environment variable %r not set" % name )
910
1006
911
- return self .string (self .env [name ]). split ( '=' , 1 )[ - 1 ]
1007
+ return self .string (self .env [name ])
912
1008
913
1009
@property
914
1010
def registers (self ):
915
- """:class:`dict`: All available registers in the coredump."""
1011
+ """:class:`dict`: All available registers in the coredump.
1012
+
1013
+ Example:
1014
+
1015
+ >>> elf = ELF.from_assembly('mov eax, 0xdeadbeef;' + shellcraft.trap(), arch='i386')
1016
+ >>> io = elf.process()
1017
+ >>> io.wait()
1018
+ >>> io.corefile.registers['eax'] == 0xdeadbeef
1019
+ True
1020
+ """
916
1021
if not self .prstatus :
917
1022
return {}
918
1023
@@ -1020,6 +1125,7 @@ def __init__(self, proc):
1020
1125
new_path = 'core.%i' % core_pid
1021
1126
if core_pid > 0 and new_path != self .core_path :
1022
1127
write (new_path , self .read (self .core_path ))
1128
+ self .unlink (self .core_path )
1023
1129
self .core_path = new_path
1024
1130
1025
1131
# Check the PID
0 commit comments