@@ -165,6 +165,18 @@ def __init__(self, leak, pointer=None, elf=None, libcdb=True):
165165 self ._waitfor = None
166166 self ._bases = {}
167167 self ._dynamic = None
168+ self .elf = None
169+
170+ if elf :
171+ path = elf
172+ if isinstance (elf , ELF ):
173+ path = elf .path
174+
175+ # Load a fresh copy of the ELF
176+ with context .local (log_level = 'error' ):
177+ w = self .waitfor ("Loading from %r" % path )
178+ self .elf = ELF (path )
179+ w .success ("[LOADED]" )
168180
169181 if not (pointer or (elf and elf .address )):
170182 log .error ("Must specify either a pointer into a module and/or an ELF file with a valid base address" )
@@ -177,12 +189,15 @@ def __init__(self, leak, pointer=None, elf=None, libcdb=True):
177189 if not elf :
178190 log .warn_once ("No ELF provided. Leaking is much faster if you have a copy of the ELF being leaked." )
179191
180- self .elf = elf
181192 self .leak = leak
182193 self .libbase = self ._find_base (pointer or elf .address )
183194
184195 if elf :
185- self ._find_linkmap_assisted (elf )
196+ self ._elftype = self .elf .elftype
197+ self ._elfclass = self .elf .elfclass
198+ self .elf .address = self .libbase
199+ self ._dynamic = self .elf .get_section_by_name ('.dynamic' ).header .sh_addr
200+ self ._dynamic = self ._make_absolute_ptr (self ._dynamic )
186201
187202 @classmethod
188203 def for_one_lib_only (cls , leak , ptr ):
@@ -241,49 +256,6 @@ def dynamic(self):
241256 self ._dynamic = self ._find_dynamic_phdr ()
242257 return self ._dynamic
243258
244- def _find_linkmap_assisted (self , path ):
245- """Uses an ELF file to assist in finding the link_map.
246- """
247- if isinstance (path , ELF ):
248- path = path .path
249-
250- # Load a fresh copy of the ELF
251- with context .local (log_level = 'error' ):
252- elf = ELF (path )
253- elf .address = self .libbase
254-
255- w = self .waitfor ("Loading from %r" % elf .path )
256-
257- # Save our real leaker
258- real_leak = self .leak
259-
260- # Create a fake leaker which just leaks out of the 'loaded' ELF
261- # However, we may load things which are outside of the ELF (e.g.
262- # the linkmap or GOT) so we need to fall back on the real leak.
263- @MemLeak
264- def fake_leak (address ):
265- try :
266- return elf .read (address , 4 )
267- except ValueError :
268- return real_leak .b (address )
269-
270- # Save off our real leaker, use the fake leaker
271- self .leak = fake_leak
272-
273- # Get useful pointers for resolving the linkmap faster
274- w .status ("Searching for DT_PLTGOT" )
275- pltgot = self ._find_dt (constants .DT_PLTGOT )
276-
277- w .status ("Searching for DT_DEBUG" )
278- debug = self ._find_dt (constants .DT_DEBUG )
279-
280- # Restore the real leaker
281- self .leak = real_leak
282-
283- # Find the linkmap using the helper pointers
284- self ._find_linkmap (pltgot , debug )
285- self .success ('Done' )
286-
287259 def _find_base (self , ptr ):
288260 page_size = 0x1000
289261 page_mask = ~ (page_size - 1 )
@@ -380,6 +352,27 @@ def _find_dynamic_phdr(self):
380352
381353 return dynamic
382354
355+ def _find_dt_optimized (self , name ):
356+ """
357+ Find an entry in the DYNAMIC array through an ELF
358+
359+ Arguments:
360+ name(str): Name of the tag to find ('DT_DEBUG', 'DT_PLTGOT', ...)
361+
362+ Returns:
363+ Pointer to the data described by the specified entry.
364+ """
365+ if not self .elf :
366+ return None
367+
368+ ptr = self .elf .dynamic_value_by_tag (name )
369+ if ptr :
370+ ptr = self ._make_absolute_ptr (ptr )
371+ self .success ("Found %s at %#x" % (name , ptr ))
372+ return ptr
373+ return None
374+
375+
383376 def _find_dt (self , tag ):
384377 """
385378 Find an entry in the DYNAMIC array.
@@ -390,11 +383,16 @@ def _find_dt(self, tag):
390383 Returns:
391384 Pointer to the data described by the specified entry.
392385 """
393- leak = self .leak
394386 base = self .libbase
395387 dynamic = self .dynamic
388+ leak = self .leak
396389 name = next (k for k ,v in ENUM_D_TAG .items () if v == tag )
397390
391+ # Read directly from the ELF if possible
392+ ptr = self ._find_dt_optimized (name )
393+ if ptr :
394+ return ptr
395+
398396 Dyn = {32 : elf .Elf32_Dyn , 64 : elf .Elf64_Dyn } [self .elfclass ]
399397
400398 # Found the _DYNAMIC program header, now find PLTGOT entry in it
@@ -407,10 +405,10 @@ def _find_dt(self, tag):
407405 self .failure ("Could not find tag %s" % name )
408406 return None
409407
410- self .status ("Found %s at %#x" % (name , dynamic ))
411408 ptr = leak .field (dynamic , Dyn .d_ptr )
412409
413410 ptr = self ._make_absolute_ptr (ptr )
411+ self .status ("Found %s at %#x" % (name , ptr ))
414412
415413 return ptr
416414
@@ -599,6 +597,10 @@ def bases(self):
599597 Return a dictionary mapping library path to its base address.
600598 '''
601599 if not self ._bases :
600+ if self .link_map is None :
601+ self .failure ("Cannot determine bases without linkmap" )
602+ return {}
603+
602604 leak = self .leak
603605 LinkMap = {32 : elf .Elf32_Link_Map , 64 : elf .Elf64_Link_Map }[self .elfclass ]
604606
@@ -666,6 +668,62 @@ def _dynamic_load_dynelf(self, libname):
666668 lib ._waitfor = self ._waitfor
667669 return lib
668670
671+ def _rel_lookup (self , symb , strtab = None , symtab = None , jmprel = None ):
672+ """Performs slower symbol lookup using DT_JMPREL(.rela.plt)"""
673+ leak = self .leak
674+ elf_obj = self .elf
675+ symb_name = symb .decode ()
676+
677+ # If elf is available look for the symbol in it
678+ if elf_obj and symb_name in elf_obj .symbols :
679+ self .success ("Symbol '%s' found in ELF!" % symb_name )
680+ return elf_obj .symbols [symb_name ]
681+
682+ log .warning ("Looking up symbol through DT_JMPREL. This might be slower..." )
683+
684+
685+ strtab = strtab or self ._find_dt (constants .DT_STRTAB )
686+ symtab = symtab or self ._find_dt (constants .DT_SYMTAB )
687+ jmprel = jmprel or self ._find_dt (constants .DT_JMPREL ) # .rela.plt
688+
689+ strtab = self ._make_absolute_ptr (strtab )
690+ symtab = self ._make_absolute_ptr (symtab )
691+ jmprel = self ._make_absolute_ptr (jmprel )
692+
693+ w = self .waitfor ("Looking for %s in .rel.plt" % symb )
694+ # We look for the symbol by iterating through each Elf64_Rel entry.
695+ # For each Elf64_Rel, get the Elf64_Sym for that entry
696+ # Then compare the Elf64_Sym.st_name with the symbol name
697+
698+ Rel = {32 : elf .Elf32_Rel , 64 : elf .Elf64_Rel }[self .elfclass ]
699+ Sym = {32 : elf .Elf32_Sym , 64 : elf .Elf64_Sym }[self .elfclass ]
700+
701+ rel_addr = jmprel
702+ rel_entry = None
703+ while True :
704+ rel_entry = leak .struct (rel_addr , Rel )
705+
706+ # We ran out of entries in DT_JMPREL
707+ if rel_entry .r_offset == 0 :
708+ return None
709+
710+ sym_idx = rel_entry .r_info >> 32 # might be different for 32-bit
711+ sym_entry_address = symtab + ( sym_idx * sizeof (Sym ) )
712+ sym_str_off = leak .field (sym_entry_address , Sym .st_name )
713+ symb_str = leak .s (strtab + sym_str_off )
714+
715+ if symb_str == symb :
716+ w .success ("Found matching Elf64_Rel entry!" )
717+ break
718+
719+ rel_addr += sizeof (Rel )
720+
721+ symbol_address = self ._make_absolute_ptr (rel_entry .r_offset )
722+
723+ return symbol_address
724+
725+
726+
669727 def _lookup (self , symb ):
670728 """Performs the actual symbol lookup within one ELF file."""
671729 leak = self .leak
@@ -698,9 +756,39 @@ def _lookup(self, symb):
698756 #
699757 # Perform the hash lookup
700758 #
759+
760+ # Save off our real leaker in case we use the fake leaker
761+ real_leak = self .leak
762+ if self .elf :
763+
764+ # Create a fake leaker which just leaks out of the 'loaded' ELF
765+ # However, we may load things which are outside of the ELF (e.g.
766+ # the linkmap or GOT) so we need to fall back on the real leak.
767+ @MemLeak
768+ def fake_leak (address ):
769+ try :
770+ return self .elf .read (address , 4 )
771+ except ValueError :
772+ return real_leak .b (address )
773+ # Use fake leaker since ELF is available
774+ self .leak = fake_leak
775+
701776 routine = {'sysv' : self ._resolve_symbol_sysv ,
702777 'gnu' : self ._resolve_symbol_gnu }[hshtype ]
703- return routine (self .libbase , symb , hshtab , strtab , symtab )
778+ resolved_addr = routine (self .libbase , symb , hshtab , strtab , symtab )
779+
780+ if resolved_addr :
781+ # Restore the original leaker
782+ self .leak = real_leak
783+ return resolved_addr
784+
785+ # if symbol not found in GNU_Hash, try looking in JMPREL
786+ resolved_addr = self ._rel_lookup (symb , strtab , symtab )
787+
788+ # Restore the original leaker
789+ self .leak = real_leak
790+
791+ return resolved_addr
704792
705793 def _resolve_symbol_sysv (self , libbase , symb , hshtab , strtab , symtab ):
706794 """
0 commit comments