3
3
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
4
4
#
5
5
6
+ import ctypes
6
7
import os , re
7
8
from typing import Any , Callable , Iterator , List , Mapping , MutableSequence , Optional , Pattern , Sequence , Tuple , Union
8
9
11
12
from qiling import Qiling
12
13
from qiling .exception import *
13
14
14
- # tuple: range start, range end, permissions mask, range label, is mmio?
15
- MapInfoEntry = Tuple [int , int , int , str , bool ]
15
+ # tuple: range start, range end, permissions mask, range label, is mmio?, bytearray
16
+ MapInfoEntry = Tuple [int , int , int , str , bool , bytearray ]
16
17
17
18
MmioReadCallback = Callable [[Qiling , int , int ], int ]
18
19
MmioWriteCallback = Callable [[Qiling , int , int , int ], None ]
@@ -80,7 +81,7 @@ def string(self, addr: int, value=None, encoding='utf-8') -> Optional[str]:
80
81
81
82
self .__write_string (addr , value , encoding )
82
83
83
- def add_mapinfo (self , mem_s : int , mem_e : int , mem_p : int , mem_info : str , is_mmio : bool = False ):
84
+ def add_mapinfo (self , mem_s : int , mem_e : int , mem_p : int , mem_info : str , is_mmio : bool = False , data : bytearray = None ):
84
85
"""Add a new memory range to map.
85
86
86
87
Args:
@@ -90,12 +91,11 @@ def add_mapinfo(self, mem_s: int, mem_e: int, mem_p: int, mem_info: str, is_mmio
90
91
mem_info: map entry label
91
92
is_mmio: memory range is mmio
92
93
"""
93
-
94
- self .map_info .append ((mem_s , mem_e , mem_p , mem_info , is_mmio ))
95
- self .map_info = sorted (self .map_info , key = lambda tp : tp [0 ])
94
+ self .map_info .append ((mem_s , mem_e , mem_p , mem_info , is_mmio , data ))
95
+ self .map_info .sort (key = lambda tp : tp [0 ])
96
96
97
97
def del_mapinfo (self , mem_s : int , mem_e : int ):
98
- """Subtract a memory range from map.
98
+ """Subtract a memory range from map, will destroy data and unmap uc mem in the range .
99
99
100
100
Args:
101
101
mem_s: memory range start
@@ -104,30 +104,36 @@ def del_mapinfo(self, mem_s: int, mem_e: int):
104
104
105
105
tmp_map_info : MutableSequence [MapInfoEntry ] = []
106
106
107
- for s , e , p , info , mmio in self .map_info :
107
+ for s , e , p , info , mmio , data in self .map_info :
108
108
if e <= mem_s :
109
- tmp_map_info .append ((s , e , p , info , mmio ))
109
+ tmp_map_info .append ((s , e , p , info , mmio , data ))
110
110
continue
111
111
112
112
if s >= mem_e :
113
- tmp_map_info .append ((s , e , p , info , mmio ))
113
+ tmp_map_info .append ((s , e , p , info , mmio , data ))
114
114
continue
115
115
116
116
if s < mem_s :
117
- tmp_map_info .append ((s , mem_s , p , info , mmio ))
117
+ self .ql .uc .mem_unmap (s , mem_s - s )
118
+ self .map_ptr (s , mem_s - s , p , data [:mem_s - s ])
119
+ tmp_map_info .append ((s , mem_s , p , info , mmio , data [:mem_s - s ]))
118
120
119
121
if s == mem_s :
120
122
pass
121
123
122
124
if e > mem_e :
123
- tmp_map_info .append ((mem_e , e , p , info , mmio ))
125
+ self .ql .uc .mem_unmap (mem_e , e - mem_e )
126
+ self .map_ptr (mem_e , e - mem_e , p , data [mem_e - e :])
127
+ tmp_map_info .append ((mem_e , e , p , info , mmio , data [mem_e - e :]))
124
128
125
129
if e == mem_e :
126
130
pass
127
131
132
+ del data [mem_s - s :mem_e - s ]
133
+
128
134
self .map_info = tmp_map_info
129
135
130
- def change_mapinfo (self , mem_s : int , mem_e : int , mem_p : Optional [int ] = None , mem_info : Optional [str ] = None ):
136
+ def change_mapinfo (self , mem_s : int , mem_e : int , mem_p : Optional [int ] = None , mem_info : Optional [str ] = None , data : Optional [ bytearray ] = None ):
131
137
tmp_map_info : Optional [MapInfoEntry ] = None
132
138
info_idx : int = None
133
139
@@ -142,12 +148,15 @@ def change_mapinfo(self, mem_s: int, mem_e: int, mem_p: Optional[int] = None, me
142
148
return
143
149
144
150
if mem_p is not None :
145
- self .del_mapinfo (mem_s , mem_e )
146
- self .add_mapinfo (mem_s , mem_e , mem_p , mem_info if mem_info else tmp_map_info [3 ])
151
+ data = data or self .read (mem_s , mem_e - mem_s )
152
+ assert (len (data ) == mem_e - mem_s )
153
+ self .unmap (mem_s , mem_e - mem_s )
154
+ self .map_ptr (mem_s , mem_e - mem_s , mem_p , data )
155
+ self .add_mapinfo (mem_s , mem_e , mem_p , mem_info or tmp_map_info [3 ], tmp_map_info [4 ], data )
147
156
return
148
157
149
158
if mem_info is not None :
150
- self .map_info [info_idx ] = (tmp_map_info [0 ], tmp_map_info [1 ], tmp_map_info [2 ], mem_info , tmp_map_info [4 ])
159
+ self .map_info [info_idx ] = (tmp_map_info [0 ], tmp_map_info [1 ], tmp_map_info [2 ], mem_info , tmp_map_info [4 ], tmp_map_info [ 5 ] )
151
160
152
161
def get_mapinfo (self ) -> Sequence [Tuple [int , int , str , str , str ]]:
153
162
"""Get memory map info.
@@ -166,7 +175,7 @@ def __perms_mapping(ps: int) -> str:
166
175
167
176
return '' .join (val if idx & ps else '-' for idx , val in perms_d .items ())
168
177
169
- def __process (lbound : int , ubound : int , perms : int , label : str , is_mmio : bool ) -> Tuple [int , int , str , str , str ]:
178
+ def __process (lbound : int , ubound : int , perms : int , label : str , is_mmio : bool , _data : bytearray ) -> Tuple [int , int , str , str , str ]:
170
179
perms_str = __perms_mapping (perms )
171
180
172
181
if hasattr (self .ql , 'loader' ):
@@ -211,7 +220,7 @@ def get_lib_base(self, filename: str) -> Optional[int]:
211
220
212
221
# some info labels may be prefixed by boxed label which breaks the search by basename.
213
222
# iterate through all info labels and remove all boxed prefixes, if any
214
- stripped = ((lbound , p .sub ('' , info )) for lbound , _ , _ , info , _ in self .map_info )
223
+ stripped = ((lbound , p .sub ('' , info )) for lbound , _ , _ , info , _ , _ in self .map_info )
215
224
216
225
return next ((lbound for lbound , info in stripped if os .path .basename (info ) == filename ), None )
217
226
@@ -268,11 +277,10 @@ def save(self):
268
277
"mmio" : []
269
278
}
270
279
271
- for lbound , ubound , perm , label , is_mmio in self .map_info :
280
+ for lbound , ubound , perm , label , is_mmio , data in self .map_info :
272
281
if is_mmio :
273
282
mem_dict ['mmio' ].append ((lbound , ubound , perm , label , * self .mmio_cbs [(lbound , ubound )]))
274
283
else :
275
- data = self .read (lbound , ubound - lbound )
276
284
mem_dict ['ram' ].append ((lbound , ubound , perm , label , bytes (data )))
277
285
278
286
return mem_dict
@@ -393,7 +401,7 @@ def search(self, needle: Union[bytes, Pattern[bytes]], begin: Optional[int] = No
393
401
assert begin < end , 'search arguments do not make sense'
394
402
395
403
# narrow the search down to relevant ranges; mmio ranges are excluded due to potential read size effects
396
- ranges = [(max (begin , lbound ), min (ubound , end )) for lbound , ubound , _ , _ , is_mmio in self .map_info if not (end < lbound or ubound < begin or is_mmio )]
404
+ ranges = [(max (begin , lbound ), min (ubound , end )) for lbound , ubound , _ , _ , is_mmio , _data in self .map_info if not (end < lbound or ubound < begin or is_mmio )]
397
405
results = []
398
406
399
407
# if needle is a bytes sequence use it verbatim, not as a pattern
@@ -439,10 +447,10 @@ def __mapped_regions(self) -> Iterator[Tuple[int, int]]:
439
447
440
448
iter_memmap = iter (self .map_info )
441
449
442
- p_lbound , p_ubound , _ , _ , _ = next (iter_memmap )
450
+ p_lbound , p_ubound , _ , _ , _ , _ = next (iter_memmap )
443
451
444
452
# map_info is assumed to contain non-overlapping regions sorted by lbound
445
- for lbound , ubound , _ , _ , _ in iter_memmap :
453
+ for lbound , ubound , _ , _ , _ , _ in iter_memmap :
446
454
if lbound == p_ubound :
447
455
p_ubound = ubound
448
456
else :
@@ -514,8 +522,8 @@ def find_free_space(self, size: int, minaddr: Optional[int] = None, maxaddr: Opt
514
522
assert minaddr < maxaddr
515
523
516
524
# get gap ranges between mapped ones and memory bounds
517
- gaps_ubounds = tuple (lbound for lbound , _ , _ , _ , _ in self .map_info ) + (mem_ubound ,)
518
- gaps_lbounds = (mem_lbound ,) + tuple (ubound for _ , ubound , _ , _ , _ in self .map_info )
525
+ gaps_ubounds = tuple (lbound for lbound , _ , _ , _ , _ , _ in self .map_info ) + (mem_ubound ,)
526
+ gaps_lbounds = (mem_lbound ,) + tuple (ubound for _ , ubound , _ , _ , _ , _ in self .map_info )
519
527
gaps = zip (gaps_lbounds , gaps_ubounds )
520
528
521
529
for lbound , ubound in gaps :
@@ -563,7 +571,7 @@ def protect(self, addr: int, size: int, perms):
563
571
self .change_mapinfo (aligned_address , aligned_address + aligned_size , mem_p = perms )
564
572
565
573
566
- def map (self , addr : int , size : int , perms : int = UC_PROT_ALL , info : Optional [str ] = None ):
574
+ def map (self , addr : int , size : int , perms : int = UC_PROT_ALL , info : Optional [str ] = None , ptr : Optional [ bytearray ] = None ):
567
575
"""Map a new memory range.
568
576
569
577
Args:
@@ -582,8 +590,25 @@ def map(self, addr: int, size: int, perms: int = UC_PROT_ALL, info: Optional[str
582
590
if not self .is_available (addr , size ):
583
591
raise QlMemoryMappedError ('Requested memory is unavailable' )
584
592
585
- self .ql .uc .mem_map (addr , size , perms )
586
- self .add_mapinfo (addr , addr + size , perms , info or '[mapped]' , is_mmio = False )
593
+ buf = self .map_ptr (addr , size , perms , ptr )
594
+ self .add_mapinfo (addr , addr + size , perms , info or '[mapped]' , is_mmio = False , data = buf )
595
+
596
+ def map_ptr (self , addr : int , size : int , perms : int = UC_PROT_ALL , buf : Optional [bytearray ] = None ) -> bytearray :
597
+ """Map a new memory range allocated as Python bytearray, will not affect map_info
598
+
599
+ Args:
600
+ addr: memory range base address
601
+ size: memory range size (in bytes)
602
+ perms: requested permissions mask
603
+ buf: bytearray already allocated (if any)
604
+
605
+ Returns:
606
+ bytearray with size, should be added to map_info by caller
607
+ """
608
+ buf = buf or bytearray (size )
609
+ buf_type = ctypes .c_byte * size
610
+ self .ql .uc .mem_map_ptr (addr , size , perms , buf_type .from_buffer (buf ))
611
+ return buf
587
612
588
613
def map_mmio (self , addr : int , size : int , read_cb : Optional [MmioReadCallback ], write_cb : Optional [MmioWriteCallback ], info : str = '[mmio]' ):
589
614
# TODO: mmio memory overlap with ram? Is that possible?
0 commit comments