View difference between Paste ID: JHUsNyUg and LjmQnrV5
SHOW: | | - or go back to the newest paste.
1
#!/bin/bash
2
set -euo pipefail
3
base_dir="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd -P)"
4
cd "$base_dir"
5
6
#region functions
7
#region init
8
function add_user_local_bin_to_path() {
9
  local expected="$HOME/.local/bin"
10
  local path
11
  path=$(echo "$PATH" | tr ':' $'\n' | sed -E 's#/+$##g')
12
  if ! echo "$path" | grep -q "$expected"; then
13
    PATH="$expected:$(echo "$path" | tr $'\n' ':')"
14
  fi
15
}
16
17
function install_bootstrap_ubuntu() {
18
  local packages=()
19
  curl --version >/dev/null 2>&1 || packages+=( curl )
20
  git --version >/dev/null 2>&1 || packages+=( git )
21
  if [[ ${#packages[@]} -gt 0 ]]; then
22
    echo "installing packages: ${packages[*]} .."
23
    sudo apt install "${packages[@]}"
24
  fi
25
}
26
27
function is_python_min_version_found() {
28
  local pythonVersion
29
  pythonVersion=$(python3 --version 2>/dev/null | awk '{ print $2 }' || echo "0.0.0")
30
  minVer=$(printf "$pythonVersion\n3.7.0" | sort --version-sort | head -n1)
31
  if [[ "$minVer" == "3.7.0" ]]; then
32
    return 0
33
  fi
34
  return 1
35
}
36
37
function install_python_macos() {
38
  if ! is_python_min_version_found; then
39
    if ! which brew >/dev/null 2>&1; then
40
      echo "error: brew not found - download from https://brew.sh/" >&2
41
      exit 1
42
    fi
43
    echo "installing python3 .." >&2
44
    HOMEBREW_NO_AUTO_UPDATE=1 brew install --quiet python3
45
  fi
46
}
47
48
function install_python_ubuntu() {
49
  local packages=()
50
  is_python_min_version_found || packages+=( python3.10 )
51
  dpkg -l | awk '$2=="python3.10-venv"&&$1=="ii"' | grep -q . \
52
    || packages+=( python3.10-venv )
53
  pip3 --version >/dev/null 2>&1 || packages+=( python3-pip )
54
  if [[ ${#packages[@]} -gt 0 ]]; then
55
    echo "installing python3 packages: ${packages[*]} .."
56
    sudo apt install "${packages[@]}"
57
  fi
58
}
59
#endregion
60
61
#region sdk
62
function get_platform_os() {
63
  case "$(uname -s | tr '[:upper:]' '[:lower:]')" in
64
    darwin )
65
      echo "mac"
66
      ;;
67
    linux )
68
      echo "linux"
69
      ;;
70
    * )
71
      echo "error: unsupported platform os '$(uname -s)' expected 'Darwin' or 'Linux'" >&2
72
      exit 1
73
  esac
74
}
75
76
function get_sdk_home() {
77
  local android_home=${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}
78
  if [[ "$android_home" == "" ]]; then
79
    case $(get_platform_os) in
80
      mac )
81
        echo "$HOME/Library/Android/sdk"
82
        ;;
83
      linux )
84
        echo "$HOME/Android/Sdk"
85
        ;;
86
      * )
87
        echo "error: unsupported platform" >&2
88
        exit 1
89
    esac
90
  fi
91
  echo "$android_home"
92
}
93
94
function ensure_android_home_exists() {
95
  if [[ ! -d "$android_home" ]]; then
96
    echo "creating android home directory: $android_home" >&2
97
    mkdir -p "$android_home"
98
  fi
99
}
100
101
function get_cmdline_tools_filename() {
102
  local platform=$(get_platform_os)
103
  local filename=$( \
104
      curl -fsSL https://developer.android.com/studio \
105
        | sed -nE "s/.*(commandlinetools-$platform-[A-Za-z0-9._-]*_latest\.zip).*/\1/p" \
106
        | head -n1 \
107
    )
108
  if [[ "$filename" == "" ]]; then
109
    echo "error: could not detect android commandlinetools filename" >&2
110
    exit 1
111
  fi
112
  echo "$filename"
113
}
114
115
function get_cmdline_tools_url() {
116
  local filename=$(get_cmdline_tools_filename)
117
  echo "https://dl.google.com/android/repository/$filename"
118
}
119
120
function ensure_cmdline_tools_exists() {
121
  local sdkmanager_path="$cmdline_tools_path/latest/bin/sdkmanager"
122
  if [[ ! -f "$sdkmanager_path" ]]; then
123
    echo "fetching cmdline-tools .." >&2
124
    local url=$(get_cmdline_tools_url)
125
    curl -fsSL "$url" -o cmdline-tools.zip
126
    unzip cmdline-tools.zip -d cmdline-tools
127
    rm cmdline-tools.zip
128
    cd cmdline-tools
129
    mv cmdline-tools latest
130
    cd - >/dev/null
131
  fi
132
  if ! which sdkmanager >/dev/null 2>&1; then
133
    PATH="$cmdline_tools_path/latest/bin:$PATH"
134
  fi
135
}
136
137
function ensure_emulator_exists() {
138
  local emulator_version=$(sdkmanager --list_installed | awk '$1 == "emulator" { print $2 }')
139
  if [[ "$emulator_version" == "" ]]; then
140
    echo "fetching emulator .." >&2
141
    echo y | sdkmanager --install emulator
142
  fi
143
  if ! which emulator >/dev/null 2>&1; then
144
    PATH="$emulator_path:$PATH"
145
  fi
146
}
147
148
function ensure_platform_tools_exists() {
149
  local platform_tools_version=$(sdkmanager --list_installed | awk '$1 == "platform-tools" { print $2 }')
150
  if [[ "$platform_tools_version" == "" ]]; then
151
    echo "fetching platform-tools .." >&2
152
    echo y | sdkmanager --install platform-tools
153
  fi
154
  if ! which adb >/dev/null 2>&1; then
155
    PATH="$platform_tools_path:$PATH"
156
  fi
157
}
158
159
function ensure_platforms_exists() {
160
  if [[ ! -d platforms ]]; then
161
    echo "creating directory platforms .." >&2
162
    mkdir platforms
163
  fi
164
}
165
166
function get_platform_arch() {
167
  case "$(uname -m)" in
168
    x86_64 )
169
      echo "x86_64"
170
      ;;
171
    arm64 )
172
      echo "arm64-v8a"
173
      ;;
174
    * )
175
      echo "error: unsupported platform arch '$(uname -m)' expected 'x86_64' or 'arm64'" >&2
176
      exit 1
177
  esac
178
}
179
180
function setup_android_sdk() {
181
  android_home=$(get_sdk_home)
182
  cmdline_tools_path="$android_home/cmdline-tools"
183
  emulator_path="$android_home/emulator"
184
  platform_tools_path="$android_home/platform-tools"
185
186
  if [[ "${ANDROID_HOME:-}" == "" ]]; then
187
    export ANDROID_HOME=$android_home
188
  fi
189
190
  if [[ "${ANDROID_SDK_ROOT:-}" == "" ]]; then
191
    export ANDROID_SDK_ROOT=$android_home
192
  fi
193
194
  ensure_android_home_exists
195
  cd "$android_home"
196
  ensure_cmdline_tools_exists
197
  ensure_emulator_exists
198
  ensure_platform_tools_exists
199
  ensure_platforms_exists
200
  cd - >/dev/null
201
}
202
#endregion
203
204
#region submodules
205
function setup_pywidevine() {
206
  cd lib
207
  if [[ ! -f pywidevine/.git/config ]]; then
208
    echo "cloning pywidevine .."
209
    git clone --quiet https://github.com/rlaphoenix/pywidevine.git
210
  fi
211
  cd pywidevine
212
  poetry --version >/dev/null 2>&1 || pip3 install poetry
213
  poetry config virtualenvs.in-project true
214
  poetry install -E serve
215
  cd "$base_dir"
216
}
217
218
function setup_dumper() {
219
  cd lib
220
  if [[ ! -f dumper/.git/config ]]; then
221
    echo "cloning dumper .."
222
    git clone --quiet https://github.com/Diazole/dumper.git
223
  fi
224
  cd dumper
225
  echo "installing dumper dependencies .."
226
  python3 -m venv venv
227
  . venv/bin/activate
228
  pip3 install -r requirements.txt
229
  deactivate
230
  cd "$base_dir"
231
}
232
233
function install_submodules() {
234
  if [[ ! -d "$base_dir/lib" ]]; then
235
    mkdir "$base_dir/lib"
236
  fi
237
  setup_pywidevine
238
  setup_dumper
239
}
240
#endregion
241
242
#region dumper
243
function ensure_frida_server_exists() {
244
  case "$(uname -m)" in
245
    x86_64 | arm64 )
246
      frida_bin="frida-server-16.0.8-android-$(uname -m)"
247
      ;;
248
    * )
249
      echo "error: unsupported platform arch '$(uname -m)' expected 'x86_64' or 'arm64'" >&2
250
      exit 1
251
  esac
252
  local frida_url="https://github.com/frida/frida/releases/download/16.0.8/$frida_bin.xz"
253
  if [[ ! -f "lib/$frida_bin" ]]; then
254
    echo "fetching $frida_bin .."
255
    curl -fsSL -o- "$frida_url" | xz -d >"lib/$frida_bin"
256
    chmod +x "lib/$frida_bin"
257
  fi
258
}
259
260
function set_android_versions() {
261
  case "${1:-9}" in
262
    9 )
263
      api_version="28"
264
      cdm_version="14.0.0"
265
      ;;
266
    10 )
267
      api_version="29"
268
      cdm_version="15.0.0"
269
      ;;
270
    * )
271
      echo "error: android version must be 9 or 10" >&2
272
      exit 1
273
  esac
274
}
275
276
function set_avd_vars() {
277
  avd_package="system-images;android-$api_version;default;$(get_platform_arch)"
278
  avd_name="Pixel_6_API_$api_version"
279
  avd_device="pixel_6"
280
}
281
282
function ensure_default_package_exists() {
283
  local package_version=$(sdkmanager --list_installed | awk '$1 == "'"$avd_package"'" { print $2 }')
284
  if [[ "$package_version" == "" ]]; then
285
    echo "fetching $avd_package .." >&2
286
    echo y | sdkmanager --install "$avd_package"
287
  fi
288
}
289
290
function dumper_exit_handler() {
291
  if [[ ${dump_keys_pid:-} != "" ]]; then
292
    echo "exiting dumper .." >&2
293
    kill "$dump_keys_pid" >/dev/null 2>&1 || true
294
  fi
295
  if [[ ${emulator_pid:-} != "" ]]; then
296
    echo "exiting emulator .." >&2
297
    kill "$emulator_pid" >/dev/null 2>&1 || true
298
    sleep 2
299
  fi
300
  echo "removing avd $avd_name .." >&2
301
  avdmanager delete avd --name "$avd_name" >/dev/null 2>&1 || true
302
}
303
304
function create_avd() {
305
  echo "creating device $avd_name .." >&2
306
  avdmanager create avd --device "$avd_device" --name "$avd_name" --package "$avd_package" --sdcard 512M
307
  ANDROID_EMULATOR_WAIT_TIME_BEFORE_KILL=0 \
308
    emulator -no-audio -no-boot-anim -no-snapshot-save -no-window -avd "$avd_name" >/dev/null &
309
  emulator_pid=$!
310
}
311
312
function copy_frida_server() {
313
  echo "copying $frida_bin .." >&2
314
  while true; do
315
    if adb push "lib/$frida_bin" /sdcard >/dev/null 2>&1; then
316
      break
317
    fi
318
    sleep 1
319
  done
320
}
321
322
function start_frida_server() {
323
  echo "starting frida server .." >&2
324
  sleep 2
325
  adb shell su 0 sh "-c 'mv /sdcard/$frida_bin /data/local/tmp/ && cd /data/local/tmp && chmod +x $frida_bin && ./$frida_bin'" &
326
  sleep 2
327
}
328
329
function start_dumper() {
330
  echo "starting dumper .." >&2
331
  cd lib/dumper
332
  . venv/bin/activate
333
  python3 dump_keys.py --cdm-version "$cdm_version" &
334
  dump_keys_pid=$!
335
  deactivate || true
336
  sleep 2
337
  cd - >/dev/null
338
}
339
340
function launch_drm_url() {
341
  echo "launching bitmovin .." >&2
342
  adb shell am start -a android.intent.action.VIEW -d https://bitmovin.com/demos/drm
343
}
344
345
function wait_for_dumper() {
346
  echo "waiting for key dump .." >&2
347
  if [[ ! -d lib/dumper/key_dumps ]]; then
348
    mkdir -p lib/dumper/key_dumps
349
  fi
350
  while true; do
351
    key=$(find lib/dumper/key_dumps -type f -name private_key.pem -newermt '-10 seconds' | head -n1)
352
    if [[ "$key" != "" ]]; then
353
      echo "success! found key at file://$PWD/$key" >&2
354
      break
355
    fi
356
    sleep 2
357
  done
358
}
359
360
function create_wvd() {
361
  echo "creating wvd file .." >&2
362
  local key_dir="$base_dir/$(dirname "$key")"
363
  local tmp_dir=$(mktemp -d)
364
  cd lib/pywidevine
365
  poetry run pywidevine create-device \
366
    --type ANDROID --level 3 \
367
    --key "$key_dir/private_key.pem" \
368
    --client_id "$key_dir/client_id.bin" \
369
    --output "$tmp_dir"
370
  cd - >/dev/null
371
  local wvd_file=$(ls -1 "$tmp_dir"/*.wvd)
372
  if [[ "$wvd_file" == "" ]]; then
373
    echo "error: could not create wvd" >&2
374
    exit 1
375
  fi
376
  local new_wvd_name="$(basename "$wvd_file" | sed 's/unknown_//' | sed 's/sdk_built_for_//')"
377
  if [[ ! -d "$base_dir/devices" ]]; then
378
    mkdir "$base_dir/devices"
379
  fi
380
  mv "$wvd_file" "$base_dir/devices/$new_wvd_name"
381
  echo "success: created device $new_wvd_name"
382
}
383
384
function dump_keys() {
385
  ensure_frida_server_exists
386
  set_android_versions "$@"
387
  set_avd_vars
388
  ensure_default_package_exists
389
  trap dumper_exit_handler exit
390
  create_avd
391
  copy_frida_server
392
  start_frida_server
393
  start_dumper
394
  launch_drm_url
395
  wait_for_dumper
396
  create_wvd
397
}
398
#endregion
399
400
function error_reporter() {
401
  echo -e "\n\033[91merror: setup failed with status $? at line $1:\033[0m" >&2
402
  echo "" >&2
403
  awk 'NR>L-3 && NR<L+2 { printf "%s%-5d %s%s\n",(NR==L?"\033[91m> ":"\033[90m  "),NR,$0,"\033[0m" }' "L=$1" "$0" >&2
404
}
405
#endregion
406
407
trap 'error_reporter $LINENO' ERR
408
409
case "$(uname -s)" in
410
  Darwin )
411
    echo "running setup for macOS .."
412
    add_user_local_bin_to_path
413
    install_python_macos
414
    install_submodules
415
    ;;
416
  Linux )
417
    [[ ! -f /etc/os-release ]] || . /etc/os-release
418
    if [[ "${ID:-}" != "ubuntu" ]]; then
419
        echo "error: unsupported distro" >&2
420
        exit 1
421
    fi
422
    echo "running setup for Ubuntu .."
423
    export PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring
424
    add_user_local_bin_to_path
425
    install_bootstrap_ubuntu
426
    install_python_ubuntu
427
    install_submodules
428
    unset PYTHON_KEYRING_BACKEND
429
    ;;
430
  * )
431
    echo "error: unsupported OS $(uname -s)" >&2
432
    exit 1
433
esac
434
435
echo "configuring the environment .." >&2
436
setup_android_sdk
437
438
echo "starting dumper .." >&2
439
dump_keys "$@"
440