@@ -165,6 +165,18 @@ def __init__(self, leak, pointer=None, elf=None, libcdb=True):
165
165
self ._waitfor = None
166
166
self ._bases = {}
167
167
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]" )
168
180
169
181
if not (pointer or (elf and elf .address )):
170
182
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):
177
189
if not elf :
178
190
log .warn_once ("No ELF provided. Leaking is much faster if you have a copy of the ELF being leaked." )
179
191
180
- self .elf = elf
181
192
self .leak = leak
182
193
self .libbase = self ._find_base (pointer or elf .address )
183
194
184
195
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 )
186
201
187
202
@classmethod
188
203
def for_one_lib_only (cls , leak , ptr ):
@@ -241,49 +256,6 @@ def dynamic(self):
241
256
self ._dynamic = self ._find_dynamic_phdr ()
242
257
return self ._dynamic
243
258
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
-
287
259
def _find_base (self , ptr ):
288
260
page_size = 0x1000
289
261
page_mask = ~ (page_size - 1 )
@@ -380,6 +352,27 @@ def _find_dynamic_phdr(self):
380
352
381
353
return dynamic
382
354
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
+
383
376
def _find_dt (self , tag ):
384
377
"""
385
378
Find an entry in the DYNAMIC array.
@@ -390,11 +383,16 @@ def _find_dt(self, tag):
390
383
Returns:
391
384
Pointer to the data described by the specified entry.
392
385
"""
393
- leak = self .leak
394
386
base = self .libbase
395
387
dynamic = self .dynamic
388
+ leak = self .leak
396
389
name = next (k for k ,v in ENUM_D_TAG .items () if v == tag )
397
390
391
+ # Read directly from the ELF if possible
392
+ ptr = self ._find_dt_optimized (name )
393
+ if ptr :
394
+ return ptr
395
+
398
396
Dyn = {32 : elf .Elf32_Dyn , 64 : elf .Elf64_Dyn } [self .elfclass ]
399
397
400
398
# Found the _DYNAMIC program header, now find PLTGOT entry in it
@@ -407,10 +405,10 @@ def _find_dt(self, tag):
407
405
self .failure ("Could not find tag %s" % name )
408
406
return None
409
407
410
- self .status ("Found %s at %#x" % (name , dynamic ))
411
408
ptr = leak .field (dynamic , Dyn .d_ptr )
412
409
413
410
ptr = self ._make_absolute_ptr (ptr )
411
+ self .status ("Found %s at %#x" % (name , ptr ))
414
412
415
413
return ptr
416
414
@@ -599,6 +597,10 @@ def bases(self):
599
597
Return a dictionary mapping library path to its base address.
600
598
'''
601
599
if not self ._bases :
600
+ if self .link_map is None :
601
+ self .failure ("Cannot determine bases without linkmap" )
602
+ return {}
603
+
602
604
leak = self .leak
603
605
LinkMap = {32 : elf .Elf32_Link_Map , 64 : elf .Elf64_Link_Map }[self .elfclass ]
604
606
@@ -666,6 +668,62 @@ def _dynamic_load_dynelf(self, libname):
666
668
lib ._waitfor = self ._waitfor
667
669
return lib
668
670
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
+
669
727
def _lookup (self , symb ):
670
728
"""Performs the actual symbol lookup within one ELF file."""
671
729
leak = self .leak
@@ -698,9 +756,39 @@ def _lookup(self, symb):
698
756
#
699
757
# Perform the hash lookup
700
758
#
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
+
701
776
routine = {'sysv' : self ._resolve_symbol_sysv ,
702
777
'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
704
792
705
793
def _resolve_symbol_sysv (self , libbase , symb , hshtab , strtab , symtab ):
706
794
"""
0 commit comments