core.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. # -*- coding: utf-8 -*-
  2. from __future__ import unicode_literals, print_function
  3. import _frida
  4. import bisect
  5. import fnmatch
  6. import numbers
  7. import sys
  8. import threading
  9. import traceback
  10. class DeviceManager(object):
  11. def __init__(self, impl):
  12. self._impl = impl
  13. def __repr__(self):
  14. return repr(self._impl)
  15. def enumerate_devices(self):
  16. return [Device(device) for device in self._impl.enumerate_devices()]
  17. def add_remote_device(self, host):
  18. return Device(self._impl.add_remote_device(host))
  19. def remove_remote_device(self, host):
  20. self._impl.remove_remote_device(host)
  21. def get_device(self, device_id):
  22. devices = self._impl.enumerate_devices()
  23. if device_id is None:
  24. return Device(devices[0])
  25. for device in devices:
  26. if device.id == device_id:
  27. return Device(device)
  28. raise _frida.InvalidArgumentError("unable to find device with id %s" % device_id)
  29. def on(self, signal, callback):
  30. self._impl.on(signal, callback)
  31. def off(self, signal, callback):
  32. self._impl.off(signal, callback)
  33. class Device(object):
  34. def __init__(self, device):
  35. self.id = device.id
  36. self.name = device.name
  37. self.icon = device.icon
  38. self.type = device.type
  39. self._impl = device
  40. def __repr__(self):
  41. return repr(self._impl)
  42. def get_frontmost_application(self):
  43. return self._impl.get_frontmost_application()
  44. def enumerate_applications(self):
  45. return self._impl.enumerate_applications()
  46. def enumerate_processes(self):
  47. return self._impl.enumerate_processes()
  48. def get_process(self, process_name):
  49. process_name_lc = process_name.lower()
  50. matching = [process for process in self._impl.enumerate_processes() if fnmatch.fnmatchcase(process.name.lower(), process_name_lc)]
  51. if len(matching) == 1:
  52. return matching[0]
  53. elif len(matching) > 1:
  54. raise _frida.ProcessNotFoundError("ambiguous name; it matches: %s" % ", ".join(["%s (pid: %d)" % (process.name, process.pid) for process in matching]))
  55. else:
  56. raise _frida.ProcessNotFoundError("unable to find process with name '%s'" % process_name)
  57. def enable_spawn_gating(self):
  58. return self._impl.enable_spawn_gating()
  59. def disable_spawn_gating(self):
  60. return self._impl.disable_spawn_gating()
  61. def enumerate_pending_spawns(self):
  62. return self._impl.enumerate_pending_spawns()
  63. def spawn(self, argv):
  64. return self._impl.spawn(argv)
  65. def input(self, target, data):
  66. self._impl.input(self._pid_of(target), data)
  67. def resume(self, target):
  68. self._impl.resume(self._pid_of(target))
  69. def kill(self, target):
  70. self._impl.kill(self._pid_of(target))
  71. def attach(self, target):
  72. return Session(self._impl.attach(self._pid_of(target)))
  73. def on(self, signal, callback):
  74. self._impl.on(signal, callback)
  75. def off(self, signal, callback):
  76. self._impl.off(signal, callback)
  77. def _pid_of(self, target):
  78. if isinstance(target, numbers.Number):
  79. return target
  80. else:
  81. return self.get_process(target).pid
  82. class FunctionContainer(object):
  83. def __init__(self):
  84. self._functions = {}
  85. """
  86. @param address is relative to container
  87. """
  88. def ensure_function(self, address):
  89. f = self._functions.get(address)
  90. if f is not None:
  91. return f
  92. return self._do_ensure_function(address)
  93. def _do_ensure_function(self, address):
  94. raise NotImplementedError("not implemented")
  95. class Session(FunctionContainer):
  96. def __init__(self, impl):
  97. super(Session, self).__init__()
  98. self._impl = impl
  99. self._modules = None
  100. self._module_map = None
  101. self._script = None
  102. def detach(self):
  103. self._impl.detach()
  104. def enumerate_modules(self):
  105. if self._modules is None:
  106. raw_modules = self._get_api().enumerate_modules()
  107. self._modules = [Module(data['name'], int(data['base'], 16), data['size'], data['path'], self) for data in raw_modules]
  108. return self._modules
  109. def prefetch_modules(self):
  110. modules = self.enumerate_modules()
  111. pending = [m for m in modules if m._exports is None]
  112. batches = self._get_api().enumerate_exports([m.path for m in pending])
  113. for i, raw_exports in enumerate(batches):
  114. pending[i]._update_exports(raw_exports)
  115. """
  116. @param protection example '--x'
  117. """
  118. def enumerate_ranges(self, protection):
  119. raw_ranges = self._get_api().enumerate_ranges(protection)
  120. return [Range(int(data['base'], 16), data['size'], data['protection']) for data in raw_ranges]
  121. def find_base_address(self, module_name):
  122. raw_base_address = self._get_api().find_base_address(module_name)
  123. return int(raw_base_address, 16)
  124. def read_bytes(self, address, size):
  125. return self._get_api().read_byte_array("0x%x" % address, size)
  126. def write_bytes(self, address, data):
  127. self._get_api().write_byte_array("0x%x" % address, [x for x in iterbytes(data)])
  128. def read_utf8(self, address, length=-1):
  129. return self._get_api().read_utf8("0x%x" % address, length)
  130. def write_utf8(self, address, string):
  131. self._get_api().write_utf8("0x%x" % address, string)
  132. def create_script(self, *args, **kwargs):
  133. return Script(self._impl.create_script(*args, **kwargs))
  134. def create_script_from_bytes(self, *args, **kwargs):
  135. return Script(self._impl.create_script_from_bytes(*args, **kwargs))
  136. def compile_script(self, *args, **kwargs):
  137. return self._impl.compile_script(*args, **kwargs)
  138. def enable_debugger(self, *args, **kwargs):
  139. return self._impl.enable_debugger(*args, **kwargs)
  140. def disable_debugger(self):
  141. return self._impl.disable_debugger()
  142. def disable_jit(self):
  143. return self._impl.disable_jit()
  144. def on(self, signal, callback):
  145. self._impl.on(signal, callback)
  146. def off(self, signal, callback):
  147. self._impl.off(signal, callback)
  148. def _get_api(self):
  149. return self._get_script().exports
  150. def _get_script(self):
  151. if self._script is None:
  152. self._script = self.create_script(name="session", source=self._create_session_script())
  153. self._script.on('message', self._on_message)
  154. self._script.load()
  155. return self._script
  156. def _on_message(self, message, data):
  157. print("[session]", message, data)
  158. def _create_session_script(self):
  159. return """\
  160. "use strict";
  161. rpc.exports = {
  162. enumerateModules: function () {
  163. return Process.enumerateModulesSync();
  164. },
  165. enumerateExports: function (modulePaths) {
  166. return modulePaths.map(function (modulePath) {
  167. return Module.enumerateExportsSync(modulePath);
  168. });
  169. },
  170. enumerateRanges: function (protection) {
  171. return Process.enumerateRangesSync(protection);
  172. },
  173. findBaseAddress: function (moduleName) {
  174. var address = Module.findBaseAddress(moduleName);
  175. return (address !== null) ? address.toString() : "0";
  176. },
  177. readByteArray: function (address, size) {
  178. return Memory.readByteArray(ptr(address), size);
  179. },
  180. writeByteArray: function (address, data) {
  181. var base = ptr(address);
  182. for (var i = 0; i !== data.length; i++) {
  183. Memory.writeU8(base.add(i), data[i]);
  184. }
  185. },
  186. readUtf8: function (address, length) {
  187. return Memory.readUtf8String(ptr(address), length);
  188. },
  189. writeUtf8: function (address, string) {
  190. Memory.writeUtf8String(ptr(address), string);
  191. },
  192. enumerateModuleExports: function (modulePath) {
  193. return Module.enumerateExportsSync(modulePath).filter(function (e) {
  194. return e.type === 'function';
  195. });
  196. },
  197. enumerateModuleRanges: function (modulePath, protection) {
  198. return Module.enumerateRangesSync(modulePath, protection);
  199. }
  200. };
  201. """
  202. def _do_ensure_function(self, absolute_address):
  203. if self._module_map is None:
  204. self._module_map = ModuleMap(self.enumerate_modules())
  205. m = self._module_map.lookup(absolute_address)
  206. if m is not None:
  207. f = m.ensure_function(absolute_address - m.base_address)
  208. else:
  209. f = Function("dsub_%x" % absolute_address, absolute_address)
  210. self._functions[absolute_address] = f
  211. return f
  212. def __getattr__(self, attr):
  213. if attr == 'session':
  214. raise KeyError("Please update your code from `.session.create_script()` to `.create_script()`")
  215. else:
  216. return getattr(super(Session, self), attr)
  217. class Script(object):
  218. def __init__(self, impl):
  219. self.exports = ScriptExports(self)
  220. self._impl = impl
  221. self._on_message_callbacks = []
  222. self._log_handler = self._on_log
  223. self._pending = {}
  224. self._next_request_id = 1
  225. self._cond = threading.Condition()
  226. impl.on('message', self._on_message)
  227. def __repr__(self):
  228. return repr(self._impl)
  229. def load(self):
  230. self._impl.load()
  231. def unload(self):
  232. self._impl.unload()
  233. def post(self, *args, **kwargs):
  234. self._impl.post(*args, **kwargs)
  235. def on(self, signal, callback):
  236. if signal == 'message':
  237. self._on_message_callbacks.append(callback)
  238. else:
  239. self._impl.on(signal, callback)
  240. def off(self, signal, callback):
  241. if signal == 'message':
  242. self._on_message_callbacks.remove(callback)
  243. else:
  244. self._impl.off(signal, callback)
  245. def set_log_handler(self, handler):
  246. if handler is not None:
  247. self._log_handler = handler
  248. else:
  249. self._log_handler = self._on_log
  250. def _rpc_request(self, *args):
  251. result = [False, None, None]
  252. def on_complete(value, error):
  253. with self._cond:
  254. result[0] = True
  255. result[1] = value
  256. result[2] = error
  257. self._cond.notifyAll()
  258. with self._cond:
  259. request_id = self._next_request_id
  260. self._next_request_id += 1
  261. self._pending[request_id] = on_complete
  262. message = ['frida:rpc', request_id]
  263. message.extend(args)
  264. self.post(message)
  265. while not result[0]:
  266. self._cond.wait()
  267. if result[2] is not None:
  268. raise result[2]
  269. return result[1]
  270. def _on_rpc_message(self, request_id, operation, params, data):
  271. if operation in ('ok', 'error'):
  272. callback = self._pending.pop(request_id)
  273. value = None
  274. error = None
  275. if operation == 'ok':
  276. value = params[0] if data is None else data
  277. else:
  278. error = RPCException(*params[0:3])
  279. callback(value, error)
  280. def _on_message(self, message, data):
  281. mtype = message['type']
  282. payload = message.get('payload', None)
  283. if mtype == 'log':
  284. level = message['level']
  285. text = payload
  286. self._log_handler(level, text)
  287. elif mtype == 'send' and isinstance(payload, list) and payload[0] == 'frida:rpc':
  288. request_id = payload[1]
  289. operation = payload[2]
  290. params = payload[3:]
  291. self._on_rpc_message(request_id, operation, params, data)
  292. else:
  293. for callback in self._on_message_callbacks[:]:
  294. try:
  295. callback(message, data)
  296. except:
  297. traceback.print_exc()
  298. def _on_log(self, level, text):
  299. if level == 'info':
  300. print(text, file=sys.stdout)
  301. else:
  302. print(text, file=sys.stderr)
  303. class RPCException(Exception):
  304. def __str__(self):
  305. return self.args[2] if len(self.args) >= 3 else self.args[0]
  306. class ScriptExports(object):
  307. def __init__(self, script):
  308. self._script = script
  309. def __getattr__(self, name):
  310. script = self._script
  311. js_name = _to_camel_case(name)
  312. def method(*args):
  313. return script._rpc_request('call', js_name, args)
  314. return method
  315. class Module(FunctionContainer):
  316. def __init__(self, name, base_address, size, path, session):
  317. super(Module, self).__init__()
  318. self.name = name
  319. self.base_address = base_address
  320. self.size = size
  321. self.path = path
  322. self._exports = None
  323. self._session = session
  324. def __repr__(self):
  325. return "Module(name=\"%s\", base_address=0x%x, size=%d, path=\"%s\")" % (self.name, self.base_address, self.size, self.path)
  326. def __hash__(self):
  327. return self.base_address.__hash__()
  328. def __cmp__(self, other):
  329. return self.base_address.__cmp__(other.base_address)
  330. def __eq__(self, other):
  331. return self.base_address == other.base_address
  332. def __ne__(self, other):
  333. return self.base_address != other.base_address
  334. def enumerate_exports(self):
  335. if self._exports is None:
  336. raw_exports = self._session._get_api().enumerate_module_exports(self.path)
  337. self._update_exports(raw_exports)
  338. return self._exports
  339. """
  340. @param protection example '--x'
  341. """
  342. def enumerate_ranges(self, protection):
  343. raw_ranges = self._session._get_script().exports.enumerate_module_ranges(self.path, protection)
  344. return [Range(int(data['base'], 16), data['size'], data['protection']) for data in raw_ranges]
  345. def _update_exports(self, exports):
  346. self._exports = []
  347. for export in exports:
  348. relative_address = int(export["address"], 16) - self.base_address
  349. mf = ModuleFunction(self, export["name"], relative_address, True)
  350. self._exports.append(mf)
  351. self._functions[relative_address] = mf
  352. def _do_ensure_function(self, relative_address):
  353. self.enumerate_exports()
  354. mf = self._functions.get(relative_address)
  355. if mf is None:
  356. mf = ModuleFunction(self, "sub_%x" % relative_address, relative_address, False)
  357. self._functions[relative_address] = mf
  358. return mf
  359. class Function(object):
  360. def __init__(self, name, absolute_address):
  361. self.name = name
  362. self.absolute_address = absolute_address
  363. def __str__(self):
  364. return self.name
  365. def __repr__(self):
  366. return "Function(name=\"%s\", absolute_address=0x%x)" % (self.name, self.absolute_address)
  367. def __hash__(self):
  368. return self.absolute_address.__hash__()
  369. def __cmp__(self, other):
  370. return self.absolute_address.__cmp__(other.absolute_address)
  371. def __eq__(self, other):
  372. return self.absolute_address == other.absolute_address
  373. def __ne__(self, other):
  374. return self.absolute_address != other.absolute_address
  375. class ModuleFunction(Function):
  376. def __init__(self, module, name, relative_address, exported):
  377. super(ModuleFunction, self).__init__(name, module.base_address + relative_address)
  378. self.module = module
  379. self.relative_address = relative_address
  380. self.exported = exported
  381. def __repr__(self):
  382. return "ModuleFunction(module=\"%s\", name=\"%s\", relative_address=0x%x)" % (self.module.name, self.name, self.relative_address)
  383. class ObjCMethod(Function):
  384. def __init__(self, mtype, cls, method, address):
  385. self.mtype = mtype
  386. self.cls = cls
  387. self.method = method
  388. self.address = address
  389. super(ObjCMethod, self).__init__(self.display_name(), address)
  390. def display_name(self):
  391. return '{mtype}[{cls} {method}]'.format(mtype=self.mtype, cls=self.cls, method=self.method)
  392. def __repr__(self):
  393. return "ObjCMethod(mtype=\"%s\", cls=\"%s\", method=\"%s\", address=0x%x)" % (self.mtype, self.cls, self.method, self.address)
  394. class Range(object):
  395. def __init__(self, base_address, size, protection):
  396. self.base_address = base_address
  397. self.size = size
  398. self.protection = protection
  399. def __repr__(self):
  400. return "Range(base_address=0x%x, size=%s, protection='%s')" % (self.base_address, self.size, self.protection)
  401. class Error(Exception):
  402. pass
  403. class AddressMap(object):
  404. def __init__(self, items, get_address, get_size):
  405. self._items = sorted(items, key=get_address)
  406. self._indices = [ get_address(item) for item in self._items ]
  407. self._get_address = get_address
  408. self._get_size = get_size
  409. def lookup(self, address):
  410. index = bisect.bisect(self._indices, address)
  411. if index == 0:
  412. return None
  413. item = self._items[index - 1]
  414. if address >= self._get_address(item) + self._get_size(item):
  415. return None
  416. return item
  417. class ModuleMap(AddressMap):
  418. def __init__(self, modules):
  419. super(ModuleMap, self).__init__(modules, lambda m: m.base_address, lambda m: m.size)
  420. class FunctionMap(AddressMap):
  421. def __init__(self, functions, get_address=lambda f: f.absolute_address):
  422. super(FunctionMap, self).__init__(functions, get_address, lambda f: 1)
  423. def _to_camel_case(name):
  424. result = ""
  425. uppercase_next = False
  426. for c in name:
  427. if c == '_':
  428. uppercase_next = True
  429. elif uppercase_next:
  430. result += c.upper()
  431. uppercase_next = False
  432. else:
  433. result += c.lower()
  434. return result
  435. if sys.version_info[0] >= 3:
  436. iterbytes = lambda x: iter(x)
  437. else:
  438. def iterbytes(data):
  439. return (ord(char) for char in data)