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,37 @@ 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
+ self .ql .uc .mem_unmap (mem_s , mem_e - mem_s )
134
+
128
135
self .map_info = tmp_map_info
129
136
130
- def change_mapinfo (self , mem_s : int , mem_e : int , mem_p : Optional [int ] = None , mem_info : Optional [str ] = None ):
137
+ 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
138
tmp_map_info : Optional [MapInfoEntry ] = None
132
139
info_idx : int = None
133
140
@@ -143,11 +150,14 @@ def change_mapinfo(self, mem_s: int, mem_e: int, mem_p: Optional[int] = None, me
143
150
144
151
if mem_p is not None :
145
152
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 ])
153
+ data = data or bytearray (mem_e - mem_s )
154
+ assert (len (data ) == mem_e - mem_s )
155
+ self .map_ptr (mem_s , mem_e - mem_s , mem_p , data )
156
+ self .add_mapinfo (mem_s , mem_e , mem_p , mem_info or tmp_map_info [3 ], tmp_map_info [4 ], data )
147
157
return
148
158
149
159
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 ])
160
+ 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
161
152
162
def get_mapinfo (self ) -> Sequence [Tuple [int , int , str , str , str ]]:
153
163
"""Get memory map info.
@@ -166,7 +176,7 @@ def __perms_mapping(ps: int) -> str:
166
176
167
177
return '' .join (val if idx & ps else '-' for idx , val in perms_d .items ())
168
178
169
- def __process (lbound : int , ubound : int , perms : int , label : str , is_mmio : bool ) -> Tuple [int , int , str , str , str ]:
179
+ def __process (lbound : int , ubound : int , perms : int , label : str , is_mmio : bool , _data : bytearray ) -> Tuple [int , int , str , str , str ]:
170
180
perms_str = __perms_mapping (perms )
171
181
172
182
if hasattr (self .ql , 'loader' ):
@@ -211,7 +221,7 @@ def get_lib_base(self, filename: str) -> Optional[int]:
211
221
212
222
# some info labels may be prefixed by boxed label which breaks the search by basename.
213
223
# 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 )
224
+ stripped = ((lbound , p .sub ('' , info )) for lbound , _ , _ , info , _ , _ in self .map_info )
215
225
216
226
return next ((lbound for lbound , info in stripped if os .path .basename (info ) == filename ), None )
217
227
@@ -268,11 +278,10 @@ def save(self):
268
278
"mmio" : []
269
279
}
270
280
271
- for lbound , ubound , perm , label , is_mmio in self .map_info :
281
+ for lbound , ubound , perm , label , is_mmio , data in self .map_info :
272
282
if is_mmio :
273
283
mem_dict ['mmio' ].append ((lbound , ubound , perm , label , * self .mmio_cbs [(lbound , ubound )]))
274
284
else :
275
- data = self .read (lbound , ubound - lbound )
276
285
mem_dict ['ram' ].append ((lbound , ubound , perm , label , bytes (data )))
277
286
278
287
return mem_dict
@@ -393,7 +402,7 @@ def search(self, needle: Union[bytes, Pattern[bytes]], begin: Optional[int] = No
393
402
assert begin < end , 'search arguments do not make sense'
394
403
395
404
# 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 )]
405
+ 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
406
results = []
398
407
399
408
# if needle is a bytes sequence use it verbatim, not as a pattern
@@ -439,10 +448,10 @@ def __mapped_regions(self) -> Iterator[Tuple[int, int]]:
439
448
440
449
iter_memmap = iter (self .map_info )
441
450
442
- p_lbound , p_ubound , _ , _ , _ = next (iter_memmap )
451
+ p_lbound , p_ubound , _ , _ , _ , _ = next (iter_memmap )
443
452
444
453
# map_info is assumed to contain non-overlapping regions sorted by lbound
445
- for lbound , ubound , _ , _ , _ in iter_memmap :
454
+ for lbound , ubound , _ , _ , _ , _ in iter_memmap :
446
455
if lbound == p_ubound :
447
456
p_ubound = ubound
448
457
else :
@@ -514,8 +523,8 @@ def find_free_space(self, size: int, minaddr: Optional[int] = None, maxaddr: Opt
514
523
assert minaddr < maxaddr
515
524
516
525
# 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 )
526
+ gaps_ubounds = tuple (lbound for lbound , _ , _ , _ , _ , _ in self .map_info ) + (mem_ubound ,)
527
+ gaps_lbounds = (mem_lbound ,) + tuple (ubound for _ , ubound , _ , _ , _ , _ in self .map_info )
519
528
gaps = zip (gaps_lbounds , gaps_ubounds )
520
529
521
530
for lbound , ubound in gaps :
@@ -582,8 +591,25 @@ def map(self, addr: int, size: int, perms: int = UC_PROT_ALL, info: Optional[str
582
591
if not self .is_available (addr , size ):
583
592
raise QlMemoryMappedError ('Requested memory is unavailable' )
584
593
585
- self .ql .uc .mem_map (addr , size , perms )
586
- self .add_mapinfo (addr , addr + size , perms , info or '[mapped]' , is_mmio = False )
594
+ buf = self .map_ptr (addr , size , perms )
595
+ self .add_mapinfo (addr , addr + size , perms , info or '[mapped]' , is_mmio = False , data = buf )
596
+
597
+ def map_ptr (self , addr : int , size : int , perms : int = UC_PROT_ALL , buf : Optional [bytearray ] = None ) -> bytearray :
598
+ """Map a new memory range allocated as Python bytearray, will not affect map_info
599
+
600
+ Args:
601
+ addr: memory range base address
602
+ size: memory range size (in bytes)
603
+ perms: requested permissions mask
604
+ buf: bytearray already allocated (if any)
605
+
606
+ Returns:
607
+ bytearray with size, should be added to map_info by caller
608
+ """
609
+ buf = buf or bytearray (size )
610
+ buf_type = ctypes .c_byte * size
611
+ self .ql .uc .mem_map_ptr (addr , size , perms , buf_type .from_buffer (buf ))
612
+ return buf
587
613
588
614
def map_mmio (self , addr : int , size : int , read_cb : Optional [MmioReadCallback ], write_cb : Optional [MmioWriteCallback ], info : str = '[mmio]' ):
589
615
# TODO: mmio memory overlap with ram? Is that possible?
0 commit comments