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.

455 lines
15 KiB

  1. /**
  2. It should be launch earlier in order to be aware of a maximun
  3. quantity of file descriptors.
  4. @author @FrenchYeti
  5. */
  6. Java.perform(function() {
  7. // ============= Config
  8. var CONFIG = {
  9. // if TRUE enable data dump
  10. printEnable: true,
  11. // if TRUE enable libc.so open/read/write hook
  12. printLibc: false,
  13. // if TRUE print the stack trace for each hook
  14. printStackTrace: false,
  15. // to filter the file path whose data want to be dumped in ASCII
  16. dump_ascii_If_Path_contains: [".log", ".xml", ".prop"],
  17. // to filter the file path whose data want to be NOT dumped in hexdump (useful for big chunk and excessive reads)
  18. dump_hex_If_Path_NOT_contains: [".png", "/proc/self/task", "/system/lib", "base.apk", "cacert"],
  19. // to filter the file path whose data want to be NOT dumped fron libc read/write (useful for big chunk and excessive reads)
  20. dump_raw_If_Path_NOT_contains: [".png", "/proc/self/task", "/system/lib", "base.apk", "cacert"]
  21. }
  22. // ============= Keep a trace of file descriptor, path, and so
  23. var TraceFD = {};
  24. var TraceFS = {};
  25. var TraceFile = {};
  26. var TraceSysFD = {};
  27. // ============= Get classes
  28. var CLS = {
  29. File: Java.use("java.io.File"),
  30. FileInputStream: Java.use("java.io.FileInputStream"),
  31. FileOutputStream: Java.use("java.io.FileOutputStream"),
  32. String: Java.use("java.lang.String"),
  33. FileChannel: Java.use("java.nio.channels.FileChannel"),
  34. FileDescriptor: Java.use("java.io.FileDescriptor"),
  35. Thread: Java.use("java.lang.Thread"),
  36. StackTraceElement: Java.use("java.lang.StackTraceElement"),
  37. AndroidDbSQLite: Java.use("android.database.sqlite.SQLiteDatabase")
  38. };
  39. var File = {
  40. new: [
  41. CLS.File.$init.overload("java.io.File", "java.lang.String"),
  42. CLS.File.$init.overload("java.lang.String"),
  43. CLS.File.$init.overload("java.lang.String", "java.lang.String"),
  44. CLS.File.$init.overload("java.net.URI"),
  45. ]
  46. };
  47. var FileInputStream = {
  48. new: [
  49. CLS.FileInputStream.$init.overload("java.io.File"),
  50. CLS.FileInputStream.$init.overload("java.io.FileDescriptor"),
  51. CLS.FileInputStream.$init.overload("java.lang.String"),
  52. ],
  53. read: [
  54. CLS.FileInputStream.read.overload(),
  55. CLS.FileInputStream.read.overload("[B"),
  56. CLS.FileInputStream.read.overload("[B", "int", "int"),
  57. ],
  58. };
  59. var FileOuputStream = {
  60. new: [
  61. CLS.FileOutputStream.$init.overload("java.io.File"),
  62. CLS.FileOutputStream.$init.overload("java.io.File", "boolean"),
  63. CLS.FileOutputStream.$init.overload("java.io.FileDescriptor"),
  64. CLS.FileOutputStream.$init.overload("java.lang.String"),
  65. CLS.FileOutputStream.$init.overload("java.lang.String", "boolean")
  66. ],
  67. write: [
  68. CLS.FileOutputStream.write.overload("[B"),
  69. CLS.FileOutputStream.write.overload("int"),
  70. CLS.FileOutputStream.write.overload("[B", "int", "int"),
  71. ],
  72. };
  73. // ============= Hook implementation
  74. File.new[1].implementation = function(a0) {
  75. prettyLog("[Java::File.new.1] New file : " + a0);
  76. var ret = File.new[1].call(this, a0);
  77. var f = Java.cast(this, CLS.File);
  78. TraceFile["f" + this.hashCode()] = a0;
  79. return ret;
  80. }
  81. File.new[2].implementation = function(a0, a1) {
  82. prettyLog("[Java::File.read.2] New file : " + a0 + "/" + a1);
  83. var ret = File.new[2].call(this, a0, a1);;
  84. var f = Java.cast(this, CLS.File);
  85. TraceFile["f" + this.hashCode()] = a0 + "/" + a1;
  86. return ret;
  87. }
  88. FileInputStream.new[0].implementation = function(a0) {
  89. var file = Java.cast(a0, CLS.File);
  90. var fname = TraceFile["f" + file.hashCode()];
  91. if (fname == null) {
  92. var p = file.getAbsolutePath();
  93. if (p !== null)
  94. fname = TraceFile["f" + file.hashCode()] = p;
  95. }
  96. if (fname == null)
  97. fname = "[unknow]"
  98. prettyLog("[Java::FileInputStream.new.0] New input stream from file (" + fname + "): ");
  99. var fis = FileInputStream.new[0].call(this, a0)
  100. var f = Java.cast(this, CLS.FileInputStream);
  101. TraceFS["fd" + this.hashCode()] = fname;
  102. var fd = Java.cast(this.getFD(), CLS.FileDescriptor);
  103. TraceFD["fd" + fd.hashCode()] = fname;
  104. return fis;
  105. }
  106. FileInputStream.read[1].implementation = function(a0) {
  107. var fname = TraceFS["fd" + this.hashCode()];
  108. var fd = null;
  109. if (fname == null) {
  110. fd = Java.cast(this.getFD(), CLS.FileDescriptor);
  111. fname = TraceFD["fd" + fd.hashCode()]
  112. }
  113. if (fname == null)
  114. fname = "[unknow]";
  115. var b = Java.array('byte', a0);
  116. prettyLog("[Java::FileInputStream.read.1] Read from file,offset (" + fname + "," + a0 + "):\n" +
  117. prettyPrint(fname, b));
  118. return FileInputStream.read[1].call(this, a0);
  119. }
  120. FileInputStream.read[2].implementation = function(a0, a1, a2) {
  121. var fname = TraceFS["fd" + this.hashCode()];
  122. var fd = null;
  123. if (fname == null) {
  124. fd = Java.cast(this.getFD(), CLS.FileDescriptor);
  125. fname = TraceFD["fd" + fd.hashCode()]
  126. }
  127. if (fname == null)
  128. fname = "[unknow]";
  129. var b = Java.array('byte', a0);
  130. prettyLog("[Java::FileInputStream.read.2] Read from file,offset,len (" + fname + "," + a1 + "," + a2 + ")\n" +
  131. prettyPrint(fname, b));
  132. return FileInputStream.read[2].call(this, a0, a1, a2);
  133. }
  134. // =============== File Output Stream ============
  135. FileOuputStream.new[0].implementation = function(a0) {
  136. var file = Java.cast(a0, CLS.File);
  137. var fname = TraceFile["f" + file.hashCode()];
  138. if (fname == null)
  139. fname = "[unknow]<File:" + file.hashCode() + ">";
  140. prettyLog("[Java::FileOuputStream.new.0] New output stream to file (" + fname + "): ");
  141. var fis = FileOuputStream.new[0].call(this, a0);
  142. TraceFS["fd" + this.hashCode()] = fname;
  143. var fd = Java.cast(this.getFD(), CLS.FileDescriptor);
  144. TraceFD["fd" + fd.hashCode()] = fname;
  145. return fis;
  146. }
  147. FileOuputStream.new[1].implementation = function(a0) {
  148. var file = Java.cast(a0, CLS.File);
  149. var fname = TraceFile["f" + file.hashCode()];
  150. if (fname == null)
  151. fname = "[unknow]";
  152. prettyLog("[Java::FileOuputStream.new.1] New output stream to file (" + fname + "): \n");
  153. var fis = FileOuputStream.new[1].call(this, a0);
  154. TraceFS["fd" + this.hashCode()] = fname;
  155. var fd = Java.cast(this.getFD(), CLS.FileDescriptor);
  156. TraceFD["fd" + fd.hashCode()] = fname;
  157. return fis;
  158. }
  159. FileOuputStream.new[2].implementation = function(a0) {
  160. var fd = Java.cast(a0, CLS.FileDescriptor);
  161. var fname = TraceFD["fd" + fd.hashCode()];
  162. if (fname == null)
  163. fname = "[unknow]";
  164. prettyLog("[Java::FileOuputStream.new.2] New output stream to FileDescriptor (" + fname + "): \n");
  165. var fis = FileOuputStream.new[1].call(this, a0)
  166. TraceFS["fd" + this.hashCode()] = fname;
  167. return fis;
  168. }
  169. FileOuputStream.new[3].implementation = function(a0) {
  170. prettyLog("[Java::FileOuputStream.new.3] New output stream to file (str=" + a0 + "): \n");
  171. var fis = FileOuputStream.new[1].call(this, a0)
  172. TraceFS["fd" + this.hashCode()] = a0;
  173. var fd = Java.cast(this.getFD(), CLS.FileDescriptor);
  174. TraceFD["fd" + fd.hashCode()] = a0;
  175. return fis;
  176. }
  177. FileOuputStream.new[4].implementation = function(a0) {
  178. prettyLog("[Java::FileOuputStream.new.4] New output stream to file (str=" + a0 + ",bool): \n");
  179. var fis = FileOuputStream.new[1].call(this, a0)
  180. TraceFS["fd" + this.hashCode()] = a0;
  181. var fd = Java.cast(this.getFD(), CLS.FileDescriptor);
  182. TraceFD["fd" + fd.hashCode()] = a0;
  183. return fis;
  184. }
  185. FileOuputStream.write[0].implementation = function(a0) {
  186. var fname = TraceFS["fd" + this.hashCode()];
  187. var fd = null;
  188. if (fname == null) {
  189. fd = Java.cast(this.getFD(), CLS.FileDescriptor);
  190. fname = TraceFD["fd" + fd.hashCode()]
  191. }
  192. if (fname == null)
  193. fname = "[unknow]";
  194. prettyLog("[Java::FileOuputStream.write.0] Write byte array (" + fname + "):\n" +
  195. prettyPrint(fname, a0));
  196. return FileOuputStream.write[0].call(this, a0);
  197. }
  198. FileOuputStream.write[1].implementation = function(a0) {
  199. var fname = TraceFS["fd" + this.hashCode()];
  200. var fd = null;
  201. if (fname == null) {
  202. fd = Java.cast(this.getFD(), CLS.FileDescriptor);
  203. fname = TraceFD["fd" + fd.hashCode()]
  204. }
  205. if (fname == null)
  206. fname = "[unknow]";
  207. prettyLog("[Java::FileOuputStream.write.1] Write int (" + fname + "): " + a0);
  208. return FileOuputStream.write[1].call(this, a0);
  209. }
  210. FileOuputStream.write[2].implementation = function(a0, a1, a2) {
  211. var fname = TraceFS["fd" + this.hashCode()];
  212. var fd = null;
  213. if (fname == null) {
  214. fd = Java.cast(this.getFD(), CLS.FileDescriptor);
  215. fname = TraceFD["fd" + fd.hashCode()]
  216. if (fname == null)
  217. fname = "[unknow], fd=" + this.hashCode();
  218. }
  219. prettyLog("[Java::FileOuputStream.write.2] Write " + a2 + " bytes from " + a1 + " (" + fname + "):\n" +
  220. prettyPrint(fname, a0));
  221. return FileOuputStream.write[2].call(this, a0, a1, a2);
  222. }
  223. // native hooks
  224. Interceptor.attach(
  225. Module.findExportByName("libc.so", "read"), {
  226. // fd, buff, len
  227. onEnter: function(args) {
  228. if (CONFIG.printLibc === true) {
  229. var bfr = args[1],
  230. sz = args[2].toInt32();
  231. var path = (TraceSysFD["fd-" + args[0].toInt32()] != null) ? TraceSysFD["fd-" + args[0].toInt32()] : "[unknow path]";
  232. prettyLog("[Libc::read] Read FD (" + path + "," + bfr + "," + sz + ")\n" +
  233. rawPrint(path, Memory.readByteArray(bfr, sz)));
  234. }
  235. },
  236. onLeave: function(ret) {
  237. }
  238. }
  239. );
  240. Interceptor.attach(
  241. Module.findExportByName("libc.so", "open"), {
  242. // path, flags, mode
  243. onEnter: function(args) {
  244. this.path = Memory.readCString(args[0]);
  245. },
  246. onLeave: function(ret) {
  247. TraceSysFD["fd-" + ret.toInt32()] = this.path;
  248. if (CONFIG.printLibc === true)
  249. prettyLog("[Libc::open] Open file '" + this.path + "' (fd: " + ret.toInt32() + ")");
  250. }
  251. }
  252. );
  253. Interceptor.attach(
  254. Module.findExportByName("libc.so", "write"), {
  255. // fd, buff, count
  256. onEnter: function(args) {
  257. if (CONFIG.printLibc === true) {
  258. var bfr = args[1],
  259. sz = args[2].toInt32();
  260. var path = (TraceSysFD["fd-" + args[0].toInt32()] != null) ? TraceSysFD["fd-" + args[0].toInt32()] : "[unknow path]";
  261. prettyLog("[Libc::write] Write FD (" + path + "," + bfr + "," + sz + ")\n" +
  262. rawPrint(path, Memory.readByteArray(bfr, sz)));
  263. }
  264. },
  265. onLeave: function(ret) {
  266. }
  267. }
  268. );
  269. // helper functions
  270. function prettyLog(str) {
  271. console.log("---------------------------\n" + str);
  272. if (CONFIG.printStackTrace === true) {
  273. printStackTrace();
  274. }
  275. }
  276. function prettyPrint(path, buffer) {
  277. if (CONFIG.printEnable === false) return "";
  278. if (contains(path, CONFIG.dump_ascii_If_Path_contains)) {
  279. return b2s(buffer);
  280. } else if (!contains(path, CONFIG.dump_hex_If_Path_NOT_contains)) {
  281. return hexdump(b2s(buffer));
  282. }
  283. return "[dump skipped by config]";
  284. }
  285. function rawPrint(path, buffer) {
  286. if (CONFIG.printEnable === false) return "";
  287. if (!contains(path, CONFIG.dump_raw_If_Path_NOT_contains)) {
  288. return hexdump(buffer);
  289. }
  290. return "[dump skipped by config]";
  291. }
  292. function contains(path, patterns) {
  293. for (var i = 0; i < patterns.length; i++)
  294. if (path.indexOf(patterns[i]) > -1) return true;
  295. return false;
  296. }
  297. function printStackTrace() {
  298. var th = Java.cast(CLS.Thread.currentThread(), CLS.Thread);
  299. var stack = th.getStackTrace(),
  300. e = null;
  301. for (var i = 0; i < stack.length; i++) {
  302. console.log("\t" + stack[i].getClassName() + "." + stack[i].getMethodName() + "(" + stack[i].getFileName() + ")");
  303. }
  304. }
  305. function isZero(block) {
  306. var m = /^[0\s]+$/.exec(block);
  307. return m != null && m.length > 0 && (m[0] == block);
  308. }
  309. function hexdump(buffer, blockSize) {
  310. blockSize = blockSize || 16;
  311. var lines = [];
  312. var hex = "0123456789ABCDEF";
  313. var prevZero = false,
  314. ctrZero = 0;
  315. for (var b = 0; b < buffer.length; b += blockSize) {
  316. var block = buffer.slice(b, Math.min(b + blockSize, buffer.length));
  317. var addr = ("0000" + b.toString(16)).slice(-4);
  318. var codes = block.split('').map(function(ch) {
  319. var code = ch.charCodeAt(0);
  320. return " " + hex[(0xF0 & code) >> 4] + hex[0x0F & code];
  321. }).join("");
  322. codes += " ".repeat(blockSize - block.length);
  323. var chars = block.replace(/[\\x00-\\x1F\\x20\n]/g, '.');
  324. chars += " ".repeat(blockSize - block.length);
  325. if (isZero(codes)) {
  326. ctrZero += blockSize;
  327. prevZero = true;
  328. } else {
  329. if (prevZero) {
  330. lines.push("\t [" + ctrZero + "] bytes of zeroes");
  331. }
  332. lines.push(addr + " " + codes + " " + chars);
  333. prevZero = false;
  334. ctrZero = 0;
  335. }
  336. }
  337. if (prevZero) {
  338. lines.push("\t [" + ctrZero + "] bytes of zeroes");
  339. }
  340. return lines.join("\\n");
  341. }
  342. function b2s(array) {
  343. var result = "";
  344. for (var i = 0; i < array.length; i++) {
  345. result += String.fromCharCode(modulus(array[i], 256));
  346. }
  347. return result;
  348. }
  349. function modulus(x, n) {
  350. return ((x % n) + n) % n;
  351. }
  352. });