mitm-emulator.sh 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. #!/usr/bin/env bash
  2. EMULATOR_EXEC="${EMULATOR_EXEC:-$HOME/Android/Sdk/emulator/emulator}"
  3. DEVICE_NAME="${DEVICE_NAME:-Pixel_2_API_30}"
  4. CONSOLE_PORT="${CONSOLE_PORT:-5554}"
  5. export ANDROID_SERIAL="${ANDROID_SERIAL:-emulator-$CONSOLE_PORT}"
  6. SSH_PORT="${SSH_PORT:-$(( $CONSOLE_PORT + 10000 ))}"
  7. APP_NAME="${APP_NAME:-org.thoughtcrime.securesms}"
  8. TCPDUMP_FILE="${TCPDUMP_FILE:-/sdcard/android.pcap}"
  9. SSLKEYLOGFILE="${SSLKEYLOGFILE:-/sdcard/keyfile.txt}"
  10. COLLATE_KEYS="${COLLATE_KEYS:-true}"
  11. MITMPROXY_SCREEN="${MITMPROXY_SCREEN:-mitmproxy}"
  12. FRIDA_SCREEN="${FRIDA_SCREEN:-frida}"
  13. TCPDUMP_SCREEN="${TCPDUMP_SCREEN:-tcpdump}"
  14. LAUNCH_PROBE='/data/data/com.termux/files/home/launch.probe'
  15. SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
  16. function launch_emulator {
  17. $EMULATOR_EXEC -avd $DEVICE_NAME -port $CONSOLE_PORT -noaudio -camera-back none -qemu -enable-kvm &
  18. }
  19. function push_scripts {
  20. adb push manage-screen.sh '/data/data/com.termux/files/home/.'
  21. }
  22. function hit_enter {
  23. adb shell input keyevent 66
  24. }
  25. function hit_tab {
  26. adb shell input keyevent 61
  27. }
  28. function hit_back {
  29. adb shell input keyevent 4
  30. }
  31. function enable_airplane_mode {
  32. adb shell settings put global airplane_mode_on 1
  33. adb shell am broadcast -a android.intent.action.AIRPLANE_MODE
  34. }
  35. function disable_airplane_mode {
  36. adb shell settings put global airplane_mode_on 0
  37. adb shell am broadcast -a android.intent.action.AIRPLANE_MODE
  38. }
  39. function get_app_user {
  40. app=$1
  41. local app_user="$(adb shell dumpsys package $app | grep 'userId=' | cut -d= -f2 | sort -u)"
  42. if [[ -z "$app_user" ]]; then
  43. echo "Couldn't find $app install, aborting">&2
  44. exit 1
  45. fi
  46. echo "$app_user"
  47. }
  48. function launch_termux {
  49. echo "launching termux"
  50. adb root >/dev/null
  51. while ! adb shell 'am start -W -n com.termux/.HomeActivity' ; do
  52. # I really wish there were a better way to wait for the OS to finish booting
  53. # (`am` fails if you try to use it too soon after sys.boot_completed)
  54. echo "failed to launch termux, trying again in 10 seconds..."
  55. sleep 10
  56. done
  57. # the emulator can sometimes be a little slow to launch the app
  58. while ! adb shell "ls $LAUNCH_PROBE" 2>/dev/null; do
  59. echo "waiting for launch.probe"
  60. sleep 5
  61. adb shell input text "touch\ $LAUNCH_PROBE" && hit_enter
  62. done
  63. echo "found launch.probe"
  64. adb shell "rm $LAUNCH_PROBE" && echo "removed launch.probe"
  65. }
  66. function launch_sshd {
  67. adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done'
  68. # Android really has no way to tell when it's booted enough to launch apps
  69. sleep 60
  70. launch_termux
  71. adb shell input text 'sshd' && hit_enter
  72. sleep 5
  73. echo forwarding $SSH_PORT to 8022
  74. adb forward tcp:$SSH_PORT tcp:8022
  75. echo forwarded
  76. }
  77. function run_termux_command {
  78. local termux_user="$(get_app_user com.termux)"
  79. if ! ssh -p $SSH_PORT $termux_user@localhost "$1" ; then
  80. echo "ssh failed to run command '$1'"
  81. echo "if ssh is complaining about host identification, you're running the emulator locally, and are okay with trusting the extra accepted localhost keys, you can fix this by running:"
  82. echo "ssh-keyscan -p $SSH_PORT localhost | sort -u >> ~/.ssh/known_hosts"
  83. echo "(you may need to restart the emulator after for this tool to work correctly)"
  84. exit 1
  85. fi
  86. }
  87. function apply_iptables {
  88. local app_uid=$(get_app_user $1)
  89. local command="LD_PRELOAD='' sudo iptables -t nat -A OUTPUT -p tcp -m owner --uid-owner $app_uid -j DNAT --to 127.0.0.1:8080 --dport "
  90. run_termux_command "$command 80"
  91. run_termux_command "$command 443"
  92. }
  93. function launch_mitmproxy {
  94. run_termux_command "./manage-screen.sh $MITMPROXY_SCREEN && screen -S $MITMPROXY_SCREEN -X stuff 'SSLKEYLOGFILE=\"$SSLKEYLOGFILE\" mitmproxy --mode transparent -k^M'"
  95. }
  96. function attach_mitmproxy {
  97. local termux_user="$(get_app_user com.termux)"
  98. ssh -p $SSH_PORT $termux_user@localhost -t "screen -rdS '$MITMPROXY_SCREEN'"
  99. }
  100. function launch_mitmd_app {
  101. local termux_home='/data/data/com.termux/files/home/'
  102. local cert_dst='/data/local/tmp/cert-der.crt'
  103. run_termux_command \
  104. "sudo cp $termux_home/.mitmproxy/mitmproxy-ca-cert.cer $cert_dst"
  105. run_termux_command "sudo chmod o+r $cert_dst"
  106. local user=$(get_app_user $APP_NAME)
  107. enable_airplane_mode
  108. adb shell am force-stop $APP_NAME
  109. adb shell pm disable $APP_NAME
  110. adb shell pm compile -f -m space $APP_NAME
  111. adb shell pm enable $APP_NAME
  112. adb shell monkey -p $APP_NAME -c android.intent.category.LAUNCHER 1
  113. # N.B.: Frida made a breaking change, where <v16 required --no-pause, whereas >= 16 that's the default, and adding it causes an unrecognized argument error
  114. screen -dmS "$FRIDA_SCREEN" frida -D $ANDROID_SERIAL --load "${SCRIPT_DIR}/frida-android-unpinning/frida-script.js" -f $APP_NAME
  115. disable_airplane_mode
  116. }
  117. function launch_tcpdump {
  118. run_termux_command "./manage-screen.sh $TCPDUMP_SCREEN && screen -S $TCPDUMP_SCREEN -X stuff 'sudo tcpdump -i any -w $TCPDUMP_FILE^M'"
  119. }
  120. function pull_keyfile {
  121. keyfile="$(basename "$SSLKEYLOGFILE")"
  122. if [ "$COLLATE_KEYS" == true ] && [ -f "$keyfile" ] ; then
  123. pullfile="$(mktemp)"
  124. sortfile="$(mktemp)"
  125. adb pull "$SSLKEYLOGFILE" "$pullfile"
  126. sort -u "$keyfile" "$pullfile" > "$sortfile"
  127. mv "$sortfile" "$keyfile"
  128. rm "$pullfile"
  129. else
  130. adb pull "$SSLKEYLOGFILE"
  131. fi
  132. }
  133. function print_help {
  134. echo "USAGE: $0 <COMMAND> [OPTIONS]"
  135. echo "where <COMMAND> is one of:"
  136. echo "emulator run the emulator, and prepare it for other commands"
  137. echo "mitm launch mitmproxy in a screen session on the emulator"
  138. echo "tcpdump launch tcpdump in a screen session on the emulator"
  139. echo "app launch app proxied through mitmproxy (disables then renables"
  140. echo " emulator network access and the app, and launches frida in a"
  141. echo " screen session on this machine)"
  142. echo "pull pull the most recent keylogfile and pcap file,"
  143. echo " generated from mitmproxy and tcpdump respectively,"
  144. echo " to the current working directory"
  145. echo "stop-mitm use pkill on the device to stop mitmproxy and tcpdump,"
  146. echo " and pkill on this machine to stop frida"
  147. echo "stop-emulator shut down the emulated device"
  148. echo "attach-mitmproxy attach this shell to the remote screen session mitmproxy is"
  149. echo " running in (use 'ctrl+a d' to detatch)"
  150. echo "ssh ssh to the device"
  151. echo "help print this help message"
  152. echo
  153. echo "The following environment variables are used to configure behavior"
  154. echo "(ordered roughly according to how likely you'll need to set it):"
  155. echo "EMULATOR_EXEC path to the emulator executable"
  156. echo " (if this is unset but ANDROID_HOME is set,"
  157. echo " this script will use a path relative to that)"
  158. echo " current value: $EMULATOR_EXEC"
  159. echo "DEVICE_NAME name of the emulator device to use"
  160. echo " current value: $DEVICE_NAME"
  161. echo "APP_NAME name of the app being MITM'd"
  162. echo " current value: $APP_NAME"
  163. echo "CONSOLE_PORT port on the host machine the emulator listens for console connections"
  164. echo " (this must be unique for each emulator running concurrently,"
  165. echo " and is expected by adb to be even)"
  166. echo " current value: $CONSOLE_PORT"
  167. echo "SSH_PORT port on the host machine to forward SSH traffic through to the emulator"
  168. echo " (this must be unique for each emulator running concurrently;"
  169. echo " defaults to CONSOLE_PORT + 10000)"
  170. echo " current value: $SSH_PORT"
  171. echo "TCPDUMP_FILE file on the emulator to store the packet capture"
  172. echo " (be sure to make this unique if capturing multiple emulators,"
  173. echo " else pulls will clobber each other)"
  174. echo " current value: $TCPDUMP_FILE"
  175. echo "SSLKEYLOGFILE file on the emulator to store TLS keys"
  176. echo " current value: $SSLKEYLOGFILE"
  177. echo "COLLATE_KEYS if true, pull adds new keys to the existing SSLKEYLOGFILE,"
  178. echo " else, pull overwites the local SSLKEYLOGFILE"
  179. echo " (n.b.: mitmproxy on the emulator will collate regardless)"
  180. echo " current value: $COLLATE_KEYS"
  181. echo "FRIDA_SCREEN name of the local screen session to run frida on"
  182. echo " current value: $FRIDA_SCREEN"
  183. echo "MITMPROXY_SCREEN name of the android screen session to run mitmproxy on"
  184. echo " current value: $MITMPROXY_SCREEN"
  185. echo "TCPDUMP_SCREEN name of the android screen session to run tcpdump on"
  186. echo " current value: $TCPDUMP_SCREEN"
  187. }
  188. case "$1" in
  189. emulator)
  190. launch_emulator;
  191. launch_sshd;
  192. apply_iptables $APP_NAME ;;
  193. mitm)
  194. push_scripts;
  195. launch_mitmproxy ;;
  196. tcpdump)
  197. push_scripts;
  198. launch_tcpdump ;;
  199. app)
  200. launch_mitmd_app ;;
  201. pull)
  202. pull_keyfile;
  203. adb pull "$TCPDUMP_FILE" ;;
  204. stop-mitm)
  205. run_termux_command 'sudo pkill tcpdump';
  206. run_termux_command 'pkill mitmproxy';
  207. pkill frida ;;
  208. stop-emulator)
  209. adb shell reboot -p;
  210. # the emulator takes some time to shut down, and can get corrupted if
  211. # you try to launch before it finishes. 20 seconds is what the GUI uses.
  212. sleep 20 ;;
  213. attach-mitmproxy)
  214. attach_mitmproxy ;;
  215. ssh)
  216. termux_user="$(get_app_user com.termux)"
  217. ssh -p $SSH_PORT $termux_user@localhost ;;
  218. h|-h|help|--help)
  219. print_help ;;
  220. *)
  221. print_help; exit 1 ;;
  222. esac