RE env for inspecting APKs
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

183 lines
4.7 KiB

  1. /*
  2. * raptor_frida_android_trace.js - Code tracer for Android
  3. * Copyright (c) 2017 Marco Ivaldi <raptor@0xdeadbeef.info>
  4. *
  5. * Frida.re JS script to trace arbitrary Java Methods and
  6. * Module functions for debugging and reverse engineering.
  7. * See https://www.frida.re/ and https://codeshare.frida.re/
  8. * for further information on this powerful tool.
  9. *
  10. * "We want to help others achieve interop through reverse
  11. * engineering" -- @oleavr
  12. *
  13. * Many thanks to @inode-, @federicodotta, @leonjza, and
  14. * @dankluev.
  15. *
  16. * Example usage:
  17. * # frida -U -f com.target.app -l raptor_frida_android_trace.js --no-pause
  18. *
  19. * Get the latest version at:
  20. * https://github.com/0xdea/frida-scripts/
  21. */
  22. // generic trace
  23. function trace(pattern)
  24. {
  25. var type = (pattern.toString().indexOf("!") === -1) ? "java" : "module";
  26. if (type === "module") {
  27. // trace Module
  28. var res = new ApiResolver("module");
  29. var matches = res.enumerateMatchesSync(pattern);
  30. var targets = uniqBy(matches, JSON.stringify);
  31. targets.forEach(function(target) {
  32. traceModule(target.address, target.name);
  33. });
  34. } else if (type === "java") {
  35. // trace Java Class
  36. var found = false;
  37. Java.enumerateLoadedClasses({
  38. onMatch: function(aClass) {
  39. if (aClass.match(pattern)) {
  40. found = true;
  41. var className = aClass.match(/[L](.*);/)[1].replace(/\//g, ".");
  42. traceClass(className);
  43. }
  44. },
  45. onComplete: function() {}
  46. });
  47. // trace Java Method
  48. if (!found) {
  49. try {
  50. traceMethod(pattern);
  51. }
  52. catch(err) { // catch non existing classes/methods
  53. console.error(err);
  54. }
  55. }
  56. }
  57. }
  58. // find and trace all methods declared in a Java Class
  59. function traceClass(targetClass)
  60. {
  61. var hook = Java.use(targetClass);
  62. var methods = hook.class.getDeclaredMethods();
  63. hook.$dispose;
  64. var parsedMethods = [];
  65. methods.forEach(function(method) {
  66. parsedMethods.push(method.toString().replace(targetClass + ".", "TOKEN").match(/\sTOKEN(.*)\(/)[1]);
  67. });
  68. var targets = uniqBy(parsedMethods, JSON.stringify);
  69. targets.forEach(function(targetMethod) {
  70. traceMethod(targetClass + "." + targetMethod);
  71. });
  72. }
  73. // trace a specific Java Method
  74. function traceMethod(targetClassMethod)
  75. {
  76. var delim = targetClassMethod.lastIndexOf(".");
  77. if (delim === -1) return;
  78. var targetClass = targetClassMethod.slice(0, delim)
  79. var targetMethod = targetClassMethod.slice(delim + 1, targetClassMethod.length)
  80. var hook = Java.use(targetClass);
  81. var overloadCount = hook[targetMethod].overloads.length;
  82. console.log("Tracing " + targetClassMethod + " [" + overloadCount + " overload(s)]");
  83. for (var i = 0; i < overloadCount; i++) {
  84. hook[targetMethod].overloads[i].implementation = function() {
  85. console.warn("\n*** entered " + targetClassMethod);
  86. // print backtrace
  87. // Java.perform(function() {
  88. // var bt = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new());
  89. // console.log("\nBacktrace:\n" + bt);
  90. // });
  91. // print args
  92. if (arguments.length) console.log();
  93. for (var j = 0; j < arguments.length; j++) {
  94. console.log("arg[" + j + "]: " + arguments[j]);
  95. }
  96. // print retval
  97. var retval = this[targetMethod].apply(this, arguments); // rare crash (Frida bug?)
  98. console.log("\nretval: " + retval);
  99. console.warn("\n*** exiting " + targetClassMethod);
  100. return retval;
  101. }
  102. }
  103. }
  104. // trace Module functions
  105. function traceModule(impl, name)
  106. {
  107. console.log("Tracing " + name);
  108. Interceptor.attach(impl, {
  109. onEnter: function(args) {
  110. // debug only the intended calls
  111. this.flag = false;
  112. // var filename = Memory.readCString(ptr(args[0]));
  113. // if (filename.indexOf("XYZ") === -1 && filename.indexOf("ZYX") === -1) // exclusion list
  114. // if (filename.indexOf("my.interesting.file") !== -1) // inclusion list
  115. this.flag = true;
  116. if (this.flag) {
  117. console.warn("\n*** entered " + name);
  118. // print backtrace
  119. console.log("\nBacktrace:\n" + Thread.backtrace(this.context, Backtracer.ACCURATE)
  120. .map(DebugSymbol.fromAddress).join("\n"));
  121. }
  122. },
  123. onLeave: function(retval) {
  124. if (this.flag) {
  125. // print retval
  126. console.log("\nretval: " + retval);
  127. console.warn("\n*** exiting " + name);
  128. }
  129. }
  130. });
  131. }
  132. // remove duplicates from array
  133. function uniqBy(array, key)
  134. {
  135. var seen = {};
  136. return array.filter(function(item) {
  137. var k = key(item);
  138. return seen.hasOwnProperty(k) ? false : (seen[k] = true);
  139. });
  140. }
  141. // usage examples
  142. setTimeout(function() { // avoid java.lang.ClassNotFoundException
  143. Java.perform(function() {
  144. // trace("com.target.utils.CryptoUtils.decrypt");
  145. // trace("com.target.utils.CryptoUtils");
  146. // trace("CryptoUtils");
  147. // trace(/crypto/i);
  148. trace("exports:*!write*");
  149. });
  150. }, 0);