discoverer.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. # -*- coding: utf-8 -*-
  2. from __future__ import unicode_literals, print_function
  3. from frida.application import await_enter
  4. from frida.core import ModuleFunction
  5. import threading
  6. class Discoverer(object):
  7. def __init__(self, reactor):
  8. self._reactor = reactor
  9. self._script = None
  10. def dispose(self):
  11. if self._script is not None:
  12. try:
  13. self._script.unload()
  14. except:
  15. pass
  16. self._script = None
  17. def start(self, session, ui):
  18. def on_message(message, data):
  19. self._reactor.schedule(lambda: self._process_message(message, data, session, ui))
  20. self._script = session.create_script(name="discoverer", source=self._create_discover_script())
  21. self._script.on('message', on_message)
  22. self._script.load()
  23. def stop(self):
  24. self._script.post({
  25. 'to': "/sampler",
  26. 'name': '+stop',
  27. 'payload': {}
  28. })
  29. def _create_discover_script(self):
  30. return """"use strict";\
  31. function Sampler() {
  32. var threadIds = [];
  33. var result = {};
  34. function onStanza(stanza) {
  35. if (stanza.to === "/sampler") {
  36. if (stanza.name === '+stop')
  37. stop();
  38. }
  39. recv(onStanza);
  40. }
  41. this.start = function () {
  42. threadIds = [];
  43. Process.enumerateThreads({
  44. onMatch: function (thread) {
  45. threadIds.push(thread.id);
  46. },
  47. onComplete: function () {
  48. threadIds.forEach(function (threadId) {
  49. Stalker.follow(threadId, {
  50. events: { call: true },
  51. onCallSummary: function (summary) {
  52. for (var address in summary) {
  53. if (summary.hasOwnProperty(address)) {
  54. var count = result[address] || 0;
  55. result[address] = count + summary[address];
  56. }
  57. }
  58. }
  59. });
  60. });
  61. send({
  62. from: "/sampler",
  63. name: '+started',
  64. payload: {
  65. total: threadIds.length
  66. }
  67. });
  68. }
  69. });
  70. };
  71. function stop() {
  72. threadIds.forEach(function (threadId) {
  73. Stalker.unfollow(threadId);
  74. });
  75. threadIds = [];
  76. send({
  77. from: "/sampler",
  78. name: '+stopped',
  79. payload: {
  80. result: result
  81. }
  82. });
  83. result = {};
  84. }
  85. recv(onStanza);
  86. };
  87. var sampler = new Sampler();
  88. setTimeout(function () { sampler.start(); }, 0);
  89. """
  90. def _process_message(self, message, data, session, ui):
  91. if message['type'] == 'send':
  92. stanza = message['payload']
  93. name = stanza['name']
  94. payload = stanza['payload']
  95. if stanza['from'] == "/sampler":
  96. if name == '+started':
  97. ui.on_sample_start(payload['total'])
  98. elif name == '+stopped':
  99. module_functions = {}
  100. dynamic_functions = []
  101. for address, count in payload['result'].items():
  102. address = int(address, 16)
  103. function = session.ensure_function(address)
  104. if isinstance(function, ModuleFunction):
  105. functions = module_functions.get(function.module, [])
  106. if len(functions) == 0:
  107. module_functions[function.module] = functions
  108. functions.append((function, count))
  109. else:
  110. dynamic_functions.append((function, count))
  111. ui.on_sample_result(module_functions, dynamic_functions)
  112. else:
  113. print(message, data)
  114. else:
  115. print(message, data)
  116. else:
  117. print(message, data)
  118. class UI(object):
  119. def on_sample_start(self, total):
  120. pass
  121. def on_sample_result(self, module_functions, dynamic_functions):
  122. pass
  123. def main():
  124. from frida.application import ConsoleApplication
  125. class DiscovererApplication(ConsoleApplication, UI):
  126. def __init__(self):
  127. self._results_received = threading.Event()
  128. ConsoleApplication.__init__(self, self._await_keys)
  129. def _await_keys(self, reactor):
  130. await_enter(reactor)
  131. reactor.schedule(lambda: self._discoverer.stop())
  132. while reactor.is_running() and not self._results_received.is_set():
  133. self._results_received.wait(0.5)
  134. def _usage(self):
  135. return "usage: %prog [options] target"
  136. def _initialize(self, parser, options, args):
  137. self._discoverer = None
  138. def _needs_target(self):
  139. return True
  140. def _start(self):
  141. self._update_status("Injecting script...")
  142. self._discoverer = Discoverer(self._reactor)
  143. self._discoverer.start(self._session, self)
  144. def _stop(self):
  145. self._print("Stopping...")
  146. self._discoverer.dispose()
  147. self._discoverer = None
  148. def on_sample_start(self, total):
  149. self._update_status("Tracing %d threads. Press ENTER to stop." % total)
  150. self._resume()
  151. def on_sample_result(self, module_functions, dynamic_functions):
  152. for module, functions in module_functions.items():
  153. self._print(module.name)
  154. self._print("\t%-10s\t%s" % ("Calls", "Function"))
  155. for function, count in sorted(functions, key=lambda item: item[1], reverse=True):
  156. self._print("\t%-10d\t%s" % (count, function))
  157. self._print("")
  158. if len(dynamic_functions) > 0:
  159. self._print("Dynamic functions:")
  160. self._print("\t%-10s\t%s" % ("Calls", "Function"))
  161. for function, count in sorted(dynamic_functions, key=lambda item: item[1], reverse=True):
  162. self._print("\t%-10d\t%s" % (count, function))
  163. self._results_received.set()
  164. app = DiscovererApplication()
  165. app.run()
  166. if __name__ == '__main__':
  167. main()