Device.java 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. package de.tudarmstadt.informatik.hostage.system;
  2. import java.io.BufferedReader;
  3. import java.io.File;
  4. import java.io.FileNotFoundException;
  5. import java.io.FileOutputStream;
  6. import java.io.IOException;
  7. import java.io.InputStream;
  8. import java.io.InputStreamReader;
  9. import java.io.OutputStream;
  10. import android.app.Activity;
  11. import android.os.Build;
  12. import android.util.Log;
  13. import de.tudarmstadt.informatik.hostage.commons.HelperUtils;
  14. import de.tudarmstadt.informatik.hostage.ui.activity.MainActivity;
  15. public class Device {
  16. private static String porthackFilepath = "/data/local/bind";
  17. private static boolean initialized = false;
  18. private static boolean root = false; // device is rooted
  19. private static boolean porthack = false; // porthack installed
  20. private static boolean iptables = false; // iptables redirection confirmed working
  21. // TODO: do asynchronous
  22. public static void checkCapabilities() {
  23. // assume worst case
  24. initialized = false;
  25. root = false;
  26. porthack = false;
  27. iptables = false;
  28. porthackFilepath = getPorthackFilepath();
  29. String porthackExists = "[ -e "+porthackFilepath+" ]"; // checks existence of porthack
  30. try {
  31. Process p = new ProcessBuilder("su", "-c", porthackExists).start();
  32. switch (p.waitFor()) {
  33. case 0: porthack = true;
  34. // fall through and don't break
  35. case 1: root = true; // 0 and 1 are valid return values of the porthack
  36. break;
  37. case 127: // command not found or executable
  38. root = false;
  39. porthack = false;
  40. break;
  41. }
  42. } catch (IOException e) {
  43. e.printStackTrace();
  44. } catch (InterruptedException e) {
  45. e.printStackTrace();
  46. }
  47. // TODO: test with various devices
  48. if (Build.VERSION.SDK_INT >= 18) { // iptables isn't fully implemented on older versions
  49. final String ipTablesList = "iptables -L -n -t nat"; // list all rules in NAT table
  50. try {
  51. Process p = new ProcessBuilder("su", "-c", ipTablesList).start();
  52. switch (p.waitFor()) {
  53. case 0: // everything is fine
  54. iptables = true; // iptables available and working
  55. break;
  56. case 3: // no such table
  57. case 127: // command not found
  58. default: // unexpected return code
  59. // while testing code 3 has been returned when table NAT is not available
  60. iptables = false;
  61. }
  62. } catch (IOException e) {
  63. e.printStackTrace();
  64. } catch (InterruptedException e) {
  65. e.printStackTrace();
  66. }
  67. }
  68. initialized = true;
  69. Log.i("TESTEST", "initialized");
  70. }
  71. public static boolean isRooted() {
  72. assert(initialized);
  73. return root;
  74. }
  75. public static boolean isPorthackInstalled() {
  76. assert(initialized);
  77. return porthack;
  78. }
  79. public static boolean isPortRedirectionAvailable() { // using iptables
  80. assert(initialized);
  81. return iptables;
  82. }
  83. /**
  84. * copies an asset to the local filesystem for later usage
  85. * (used for port hack and shell scripts)
  86. * @param assetFilePath
  87. * @param destFilePath
  88. * @return true on success
  89. */
  90. private static boolean deployAsset(String assetFilePath, String destFilePath) {
  91. Activity activity = MainActivity.getInstance();
  92. File file = new File(activity.getFilesDir(), destFilePath);
  93. try {
  94. OutputStream os = new FileOutputStream(file);
  95. try {
  96. InputStream is = activity.getAssets().open(assetFilePath);
  97. byte[] buffer = new byte[4096];
  98. int bytesRead;
  99. while ((bytesRead = is.read(buffer)) != -1) {
  100. os.write(buffer, 0, bytesRead);
  101. }
  102. is.close();
  103. } catch (IOException e) {
  104. e.printStackTrace();
  105. }
  106. os.close();
  107. } catch (FileNotFoundException e) {
  108. e.printStackTrace();
  109. return false;
  110. } catch (IOException e) {
  111. e.printStackTrace();
  112. return false;
  113. }
  114. //Log.i("FILEPATH", file.getAbsolutePath());
  115. return true; // SUCCESS!
  116. }
  117. public static void executePortRedirectionScript() {
  118. assert(iptables); // we need iptables for our next trick
  119. if (deployAsset("payload/redirect-ports.sh", "redirect-ports.sh")) {
  120. String scriptFilePath = new File(MainActivity.getInstance().getFilesDir(), "redirect-ports.sh").getAbsolutePath();
  121. Process p = null;
  122. try {
  123. p = new ProcessBuilder("su", "-c", "sh "+scriptFilePath).start();
  124. p.waitFor(); // stall the main thread
  125. // TODO: check return value?
  126. } catch (IOException e) {
  127. e.printStackTrace();
  128. } catch (InterruptedException e) {
  129. e.printStackTrace();
  130. }
  131. }
  132. }
  133. /**
  134. * @return name of porthack binary for device architecture
  135. */
  136. private static String getPorthackName() {
  137. String porthack = "bind";
  138. // determine system architecture to return the correct binary
  139. String arch = System.getProperty("os.arch");
  140. // TODO: handle more architectures
  141. if (arch.equals("i686")) { // this is what genymotion reports
  142. porthack += ".x86";
  143. } else if (arch.startsWith("arm")) {
  144. /*
  145. possible values:
  146. armv4
  147. armv4t
  148. armv5t
  149. armv5te
  150. armv5tej
  151. armv6
  152. armv7
  153. */
  154. porthack += ".arm";
  155. } else if (arch.equals("mips")) {
  156. porthack += ".mips";
  157. }
  158. return porthack;
  159. }
  160. /**
  161. * @return filepath to deployed porthack binary
  162. */
  163. public static String getPorthackFilepath() {
  164. File file = new File(MainActivity.getInstance().getFilesDir(), getPorthackName());
  165. return file.getAbsolutePath();
  166. }
  167. public static boolean deployPorthack() {
  168. String porthack = getPorthackName();
  169. if (!deployAsset("payload/"+porthack, porthack)) {
  170. return false; // :(
  171. }
  172. // make port hack executable
  173. try {
  174. Process p = new ProcessBuilder("su", "-c", "chmod 700 "+getPorthackFilepath()).start();
  175. if (p.waitFor() != 0) {
  176. logError(p.getErrorStream());
  177. return false;
  178. }
  179. logOutput(p.getInputStream());
  180. } catch (IOException e) {
  181. return false;
  182. } catch (InterruptedException e) {
  183. return false;
  184. }
  185. return true; // SUCCESS!
  186. }
  187. public static void uninstallPorthack() {try {
  188. Process p = new ProcessBuilder("su", "-c", "rm "+getPorthackFilepath()).start();
  189. if (p.waitFor() != 0) {
  190. logError(p.getErrorStream());
  191. }
  192. logOutput(p.getInputStream());
  193. } catch (IOException e) {
  194. } catch (InterruptedException e) {
  195. }
  196. }
  197. // copied from PrivilegedPort.java
  198. private static void logOutput(InputStream stdout) throws IOException {
  199. BufferedReader reader = new BufferedReader(new InputStreamReader(stdout));
  200. String line;
  201. while ((line = reader.readLine()) != null) {
  202. Log.i("HelperUtils", line);
  203. }
  204. }
  205. private static void logError(InputStream stderr) throws IOException {
  206. BufferedReader reader = new BufferedReader(new InputStreamReader(stderr));
  207. Log.e("HelperUtils", reader.readLine());
  208. }
  209. }