Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/archinfo/arch_soot.py: 51%

211 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-25 06:15 +0000

1import logging 

2import re 

3 

4from .arch import Arch, Endness, register_arch 

5 

6log = logging.getLogger("archinfo.arch_soot") 

7 

8 

9class SootMethodDescriptor: 

10 __slots__ = ["class_name", "name", "params", "_soot_method", "ret"] 

11 

12 def __init__(self, class_name, name, params, soot_method=None, ret_type=None): 

13 self.class_name = class_name 

14 self.name = name 

15 self.params = params 

16 self._soot_method = soot_method 

17 self.ret = ret_type 

18 

19 def __repr__(self): 

20 return "{}.{}({})".format(self.class_name, self.name, ", ".join(self.params)) 

21 

22 def __hash__(self): 

23 return hash((self.class_name, self.name, self.params)) 

24 

25 def __eq__(self, other): 

26 return ( 

27 isinstance(other, SootMethodDescriptor) 

28 and self.class_name == other.class_name 

29 and self.name == other.name 

30 and self.params == other.params 

31 ) 

32 

33 def __ne__(self, other): 

34 return not self == other 

35 

36 def __lt__(self, other): 

37 return self.__repr__() < other.__repr__() 

38 

39 def __gt__(self, other): 

40 return self.__repr__() > other.__repr__() 

41 

42 def __le__(self, other): 

43 return self.__repr__() <= other.__repr__() 

44 

45 def __ge__(self, other): 

46 return self.__repr__() >= other.__repr__() 

47 

48 def address(self, block_idx=0, stmt_idx=0): 

49 """ 

50 :return Address of the method. 

51 :rtype: SootAddressDescriptor 

52 """ 

53 return SootAddressDescriptor(self, block_idx, stmt_idx) 

54 

55 @property 

56 def fullname(self): 

57 """ 

58 :return the full name of the method (class name + method name) 

59 """ 

60 return f"{self.class_name}.{self.name}" 

61 

62 @property 

63 def symbolic(self): 

64 return False 

65 

66 @property 

67 def is_loaded(self): 

68 """ 

69 :return: True, if the method is loaded in CLE and thus infos about attrs, 

70 ret and exceptions are available. 

71 """ 

72 return self._soot_method is not None 

73 

74 @property 

75 def attrs(self): 

76 return self._soot_method.attrs if self.is_loaded else [] 

77 

78 @property 

79 def exceptions(self): 

80 return self._soot_method.exceptions if self.is_loaded else [] 

81 

82 @property 

83 def block_by_label(self): 

84 return self._soot_method.block_by_label if self.is_loaded else None 

85 

86 # @property 

87 # def ret(self): 

88 # return self._soot_method.ret if self.is_loaded else [] 

89 

90 @property 

91 def addr(self): 

92 """ 

93 :return: the soot address description of the entry point of the method 

94 """ 

95 return SootAddressDescriptor(self, 0, 0) 

96 

97 def matches_with_native_name(self, native_method): 

98 """ 

99 The name of native methods are getting encoded, s.t. they translate into 

100 valid C function names. This method indicates if the name of the given 

101 native method matches the name of the soot method. 

102 

103 :return: True, if name of soot method matches the mangled native name. 

104 """ 

105 

106 if "__" in native_method: 

107 # if native methods are overloaded, two underscores are used 

108 native_method, params_sig = native_method.split("__") 

109 params = ArchSoot.decode_parameter_list_signature(params_sig) 

110 # check function signature 

111 if params != self.params: 

112 return False 

113 

114 # demangle native name 

115 native_method = native_method.replace("_1", "_") 

116 # TODO unicode escaping 

117 

118 method_native_name = "Java_{class_name}_{method_name}".format( 

119 class_name=self.class_name.replace(".", "_"), method_name=self.name 

120 ) 

121 

122 return native_method == method_native_name 

123 

124 @classmethod 

125 def from_string(cls, tstr): 

126 # this should be the opposite of repr 

127 tstr = tstr.strip() 

128 class_and_method, tparams = tstr.split("(") 

129 params_str = tparams.split(")")[0] 

130 if params_str == "": 

131 params = () 

132 else: 

133 params = tuple(t.strip() for t in params_str.split(",")) 

134 class_name, _, method = class_and_method.rpartition(".") 

135 return cls(class_name, method, params) 

136 

137 @classmethod 

138 def from_soot_method(cls, soot_method): 

139 return cls( 

140 class_name=soot_method.class_name, 

141 name=soot_method.name, 

142 params=soot_method.params, 

143 soot_method=soot_method, 

144 ret_type=soot_method.ret, 

145 ) 

146 

147 

148class SootAddressDescriptor: 

149 __slots__ = ["method", "block_idx", "stmt_idx"] 

150 

151 def __init__(self, method, block_idx, stmt_idx): 

152 if not isinstance(method, SootMethodDescriptor): 

153 raise ValueError('The parameter "method" must be an ' "instance of SootMethodDescriptor.") 

154 

155 self.method = method 

156 self.block_idx = block_idx 

157 self.stmt_idx = stmt_idx 

158 

159 def __repr__(self): 

160 return "<{!r}+({}:{})>".format( 

161 self.method, self.block_idx, "%d" % self.stmt_idx if self.stmt_idx is not None else "[0]" 

162 ) 

163 

164 def __hash__(self): 

165 return hash((self.method, self.stmt_idx)) 

166 

167 def __eq__(self, other): 

168 return ( 

169 isinstance(other, SootAddressDescriptor) 

170 and self.method == other.method 

171 and self.block_idx == other.block_idx 

172 and self.stmt_idx == other.stmt_idx 

173 ) 

174 

175 def __ne__(self, other): 

176 return not self == other 

177 

178 def __lt__(self, other): 

179 return self.__repr__() < other.__repr__() 

180 

181 def __gt__(self, other): 

182 return self.__repr__() > other.__repr__() 

183 

184 def __le__(self, other): 

185 return self.__repr__() <= other.__repr__() 

186 

187 def __ge__(self, other): 

188 return self.__repr__() >= other.__repr__() 

189 

190 def __add__(self, stmts_offset): 

191 if not isinstance(stmts_offset, int): 

192 raise TypeError("The stmts_offset must be an int or a long.") 

193 s = self.copy() 

194 s.stmt_idx += stmts_offset 

195 return s 

196 

197 def copy(self): 

198 return SootAddressDescriptor(method=self.method, block_idx=self.block_idx, stmt_idx=self.stmt_idx) 

199 

200 @property 

201 def symbolic(self): 

202 return False 

203 

204 

205class SootAddressTerminator(SootAddressDescriptor): 

206 __slots__ = [] 

207 

208 def __init__(self): 

209 dummy_method = SootMethodDescriptor("dummy", "dummy", tuple()) 

210 super().__init__(dummy_method, 0, 0) 

211 

212 def __repr__(self): 

213 return "<Terminator>" 

214 

215 

216class SootFieldDescriptor: 

217 __slots__ = ["class_name", "name", "type"] 

218 

219 def __init__(self, class_name, name, type_): 

220 self.class_name = class_name 

221 self.name = name 

222 self.type = type_ 

223 

224 def __repr__(self): 

225 return f"{self.class_name}.{self.name}" 

226 

227 def __hash__(self): 

228 return hash((self.class_name, self.name, self.type)) 

229 

230 def __eq__(self, other): 

231 return ( 

232 isinstance(other, SootFieldDescriptor) 

233 and self.class_name == other.class_name 

234 and self.name == other.name 

235 and self.type == other.type 

236 ) 

237 

238 def __ne__(self, other): 

239 return not self == other 

240 

241 

242class SootClassDescriptor: 

243 __slots__ = ["name", "_soot_class"] 

244 

245 def __init__(self, name, soot_class=None): 

246 self.name = name 

247 self._soot_class = soot_class 

248 

249 def __repr__(self): 

250 return self.name 

251 

252 def __hash__(self): 

253 return hash(self.name) 

254 

255 def __eq__(self, other): 

256 return isinstance(other, SootClassDescriptor) and self.name == other.name 

257 

258 def __ne__(self, other): 

259 return not self == other 

260 

261 @property 

262 def is_loaded(self): 

263 """ 

264 :return: True, if the class is loaded in CLE and thus info about field, 

265 methods, ... are available. 

266 """ 

267 return self._soot_class is not None 

268 

269 @property 

270 def fields(self): 

271 return self._soot_class.fields if self.is_loaded else None 

272 

273 @property 

274 def methods(self): 

275 return self._soot_class.methods if self.is_loaded else None 

276 

277 @property 

278 def superclass_name(self): 

279 return self._soot_class.super_class if self.is_loaded else None 

280 

281 @property 

282 def type(self): 

283 return "java.lang.Class" 

284 

285 

286class SootNullConstant: 

287 def __init__(self): 

288 pass 

289 

290 def __repr__(self): 

291 return "null" 

292 

293 def __hash__(self): 

294 return hash("null") 

295 

296 def __eq__(self, other): 

297 return isinstance(other, SootNullConstant) 

298 

299 def __ne__(self, other): 

300 return not self == other 

301 

302 

303class SootArgument: 

304 """ 

305 Typed Java argument. 

306 """ 

307 

308 __slots__ = ["value", "type", "is_this_ref"] 

309 

310 def __init__(self, value, type_, is_this_ref=False): 

311 """ 

312 :param value: Value of the argument 

313 :param type_: Type of the argument 

314 :param is_this_ref: Indicates whether the argument represents the 

315 'this' reference, i.e. the object on which an 

316 instance method is invoked. 

317 """ 

318 self.value = value 

319 self.type = type_ 

320 self.is_this_ref = is_this_ref 

321 

322 def __repr__(self): 

323 return f"{self.value} ({self.type})" 

324 

325 

326class ArchSoot(Arch): 

327 def __init__(self, endness=Endness.LE): 

328 super().__init__(endness) 

329 

330 name = "Soot" 

331 

332 vex_arch = None # No VEX support 

333 qemu_name = None # No Qemu/Unicorn-engine support 

334 bits = 64 

335 address_types = (SootAddressDescriptor,) 

336 function_address_types = (SootMethodDescriptor,) 

337 

338 # Size of native counterparts of primitive Java types 

339 sizeof = {"boolean": 8, "byte": 8, "char": 16, "short": 16, "int": 32, "long": 64, "float": 32, "double": 64} 

340 

341 primitive_types = ["boolean", "byte", "char", "short", "int", "long", "float", "double"] 

342 

343 sig_dict = { 

344 "Z": "boolean", 

345 "B": "byte", 

346 "C": "char", 

347 "S": "short", 

348 "I": "int", 

349 "J": "long", 

350 "F": "float", 

351 "D": "double", 

352 "V": "void", 

353 } 

354 

355 @staticmethod 

356 def decode_type_signature(type_sig): 

357 if not type_sig: 

358 return None 

359 # try to translate signature as a primitive type 

360 if type_sig in ArchSoot.sig_dict: 

361 return ArchSoot.sig_dict[type_sig] 

362 # java classes are encoded as 'Lclass_name;' 

363 if type_sig.startswith("L") and type_sig.endswith(";"): 

364 return type_sig[1:-1] 

365 raise ValueError(type_sig) 

366 

367 @staticmethod 

368 def decode_parameter_list_signature(param_sig): 

369 return tuple( 

370 ArchSoot.decode_type_signature(param) for param in re.findall(r"([\[]*[ZBCSIJFDV]|[\[]*L.+;)", param_sig) 

371 ) 

372 

373 @staticmethod 

374 def decode_method_signature(method_sig): 

375 # signature format follows the pattern: (param_sig)ret_sig 

376 match = re.search(r"\((.*)\)(.*)", method_sig) 

377 param_sig, ret_sig = match.group(1), match.group(2) 

378 # decode types 

379 params_types = ArchSoot.decode_parameter_list_signature(param_sig) 

380 ret_type = ArchSoot.decode_type_signature(ret_sig) 

381 log.debug("Decoded method signature '%s' as params=%s and ret=%s", method_sig, params_types, ret_type) 

382 return params_types, ret_type 

383 

384 def library_search_path(self, pedantic=False): 

385 """ 

386 Since Java is mostly system independent, we cannot return system 

387 specific paths. 

388 

389 :return: empty list 

390 """ 

391 return [] 

392 

393 

394register_arch(["soot"], 8, Endness.LE, ArchSoot)