-
Notifications
You must be signed in to change notification settings - Fork 195
/
Copy pathapk.sh
executable file
·817 lines (761 loc) · 26.4 KB
/
apk.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
#!/bin/bash
#
# apk.sh v1.0.10
# author: ax - github.com/ax
#
# -----------------------------------------------------------------------------
#
# SYNOPSIS
# apk.sh [SUBCOMMAND] [APK FILE|APK DIR|PKG NAME] [FLAGS]
# apk.sh pull [PKG NAME] [FLAGS]
# apk.sh decode [APK FILE] [FLAGS]
# apk.sh build [APK DIR] [FLAGS]
# apk.sh patch [APK FILE] [FLAGS]
# apk.sh rename [APK FILE] [PKG NAME] [FLAGS]
#
# SUBCOMMANDS
# pull Pull an apk from device/emulator.
# decode Decode an apk.
# build Re-build an apk.
# patch Patch an apk.
# rename Rename the apk package.
#
# FLAGS
# -a, --arch <arch> Specify the target architecture, mandatory when patching.
#
# -g, --gadget-conf <json_file>
# Specify a frida-gadget configuration file, optional when patching.
#
# -n, --net Add a permissing network security config when building, optional.
# It can be used with patch, pull and rename also.
#
# -s, --safe Do not decode resources when decoding (i.e. apktool -r).
# Cannot be used when patching.
#
# -d, --no-dis Do not disassemble dex, optional when decoding (i.e. apktool -s).
# Cannot be used when patching.
#
# --only-main-classes Only disassemble the main dex classes, optional when decoding with apktool (i.e. apktool d --only-main-classes).
#
# -----------------------------------------------------------------------------
#
VERSION="1.0.10"
echo -e "[*] \033[1mapk.sh v$VERSION \033[0m"
APK_SH_HOME="${HOME}/.apk.sh"
mkdir -p $APK_SH_HOME
echo "[*] home dir is $APK_SH_HOME"
supported_arch=("arm" "x86_64" "x86" "arm64")
print_(){
:
#echo $1
}
print_ "[*] DEBUG is TRUE"
APKTOOL_VER=`wget https://api.github.com/repos/iBotPeaches/Apktool/releases/latest -q -O - | grep -Po "tag_name\": \"v\K.*?(?=\")"`
APKTOOL_PATH="$APK_SH_HOME/apktool_$APKTOOL_VER.jar"
BUILDTOOLS_VER="33.0.1"
SDK_ROOT="$APK_SH_HOME/sdk_root"
BUILD_TOOLS="$SDK_ROOT/build-tools/$BUILDTOOLS_VER"
PLATFORM_TOOLS="$SDK_ROOT/platform-tools"
if [ ! -d "$BUILD_TOOLS" ]; then
APKSIGNER="apksigner"
ZIPALIGN="zipalign"
AAPT="aapt"
else
APKSIGNER="$BUILD_TOOLS/apksigner"
ZIPALIGN="$BUILD_TOOLS/zipalign"
AAPT="$BUILD_TOOLS/aapt"
fi
if [ ! -d "$PLATFORM_TOOLS" ]; then
ADB="adb"
else
ADB="$PLATFORM_TOOLS/adb"
fi
CMDLINE_TOOLS_DIR="$APK_SH_HOME/cmdline-tools"
install_cmdlinetools() {
CMDLINE_TOOLS_DOWNLOAD_URL="https://dl.google.com/android/repository/commandlinetools-linux-9123335_latest.zip"
echo "[>] Downloading Android commandline tools from $CMDLINE_TOOLS_DOWNLOAD_URL"
wget $CMDLINE_TOOLS_DOWNLOAD_URL -q --show-progress -P $APK_SH_HOME
unzip $APK_SH_HOME/commandlinetools-linux-9123335_latest.zip -d $APK_SH_HOME
rm $APK_SH_HOME/commandlinetools-linux-9123335_latest.zip
echo "[>] Done!"
}
install_buildtools(){
if [ ! -d "$CMDLINE_TOOLS_DIR" ]; then
install_cmdlinetools
fi
SDK_MANAGER_BIN="$CMDLINE_TOOLS_DIR/bin/sdkmanager"
mkdir -p $SDK_ROOT
INSTALL_BUILDTOOLS_CMD="echo -ne 'y\n' | $SDK_MANAGER_BIN 'build-tools;$BUILDTOOLS_VER' --sdk_root=$SDK_ROOT"
echo -e "[>] Installing build-tools $BUILDTOOLS_VER..."
run "$INSTALL_BUILDTOOLS_CMD"
APKSIGNER="$BUILD_TOOLS/apksigner"
ZIPALIGN="$BUILD_TOOLS/zipalign"
AAPT="$BUILD_TOOLS/aapt"
echo "[>] Done!"
}
install_platformtools(){
if [ ! -d "$CMDLINE_TOOLS_DIR" ]; then
install_cmdlinetools
fi
SDK_MANAGER_BIN="$CMDLINE_TOOLS_DIR/bin/sdkmanager"
mkdir -p $SDK_ROOT
INSTALL_PLATFORMTOOLS_CMD="echo -ne 'y\n' | $SDK_MANAGER_BIN 'platform-tools' --sdk_root=$SDK_ROOT"
echo -e "[>] Installing platform-tools ..."
run "$INSTALL_PLATFORMTOOLS_CMD"
ADB="$PLATFORM_TOOLS/adb"
echo "[>] Done!"
}
check_apk_tools(){
if [ -f "$APKTOOL_PATH" ]; then
echo "[*] apktool v$APKTOOL_VER exist in $APK_SH_HOME"
else
APKTOOL_DOWNLOAD_URL_BB="https://bitbucket.org/iBotPeaches/apktool/downloads/apktool_$APKTOOL_VER.jar"
APKTOOL_DOWNLOAD_URL_GH="https://github.com/iBotPeaches/Apktool/releases/download/v$APKTOOL_VER/apktool_$APKTOOL_VER.jar"
APKTOOL_DOWNLOAD_URL=$APKTOOL_DOWNLOAD_URL_GH
echo "[!] No apktool v$APKTOOL_VER found!"
echo "[>] Downloading apktool from $APKTOOL_DOWNLOAD_URL"
wget $APKTOOL_DOWNLOAD_URL -q --show-progress -P $APK_SH_HOME
fi
if is_not_installed 'apksigner'; then
if [ ! -f "$APKSIGNER" ]; then
echo "[!] No apksigner found in path!"
echo "[!] No apksigner found in $APK_SH_HOME"
install_buildtools
echo "[>] apksigner installed!"
else
echo "[*] apksigner v`$APKSIGNER --version` exist in $BUILD_TOOLS"
fi
fi
if is_not_installed 'zipalign'; then
if [ ! -f "$ZIPALIGN" ]; then
install_buildtools
echo "[>] zipalign installed!"
else
echo "[*] zipalign exist in $BUILD_TOOLS"
fi
fi
if is_not_installed 'aapt'; then
if [ ! -f "$AAPT" ]; then
install_buildtools
echo "[>] aapt installed!"
else
echo "[*] aapt exist in $BUILD_TOOLS"
fi
fi
if is_not_installed 'adb'; then
if [ ! -f "$ADB" ]; then
install_platformtools
echo "[>] adb installed!"
else
echo "[*] adb exist in $PLATFORM_TOOLS"
fi
fi
if is_not_installed 'unxz'; then
echo "[>] No unxz found!"
echo "[>] Pls install unxz!"
exit 1
fi
if is_not_installed 'unzip'; then
echo "[>] No unzip found!"
echo "[>] Pls install unzip!"
exit 1
fi
return 0
}
is_not_installed() {
if [ -z `command -v $1 2>/dev/null` ]; then
return 0
fi
return 1
}
run(){
if ! eval "$1"; then
echo "[>] Sorry!"
echo "[!] $1 return errors!"
echo "[>] Bye!"
exit 1
fi
}
exit_if_not_exist(){
if [ ! -f "$1" ]; then
if [ ! -d "$1" ]; then
echo "[!] File $1 not found!"
echo "[>] Bye!"
exit 1
fi
fi
}
apk_decode(){
APK_NAME="$1"
DECODE_CMD_OPTS="$2"
DECODE_CMD_START="java -jar $APKTOOL_PATH d"
DECODE_CMD="$DECODE_CMD_START $APK_NAME $DECODE_CMD_OPTS"
echo -e "[>] \033[1mDecoding $APK_NAME\033[0m with $DECODE_CMD"
run "$DECODE_CMD"
echo "[>] Done!"
}
apk_build(){
APK_DIR="$1"
BUILD_CMD_OPTS="$2"
BUILD_CMD_START="java -jar $APKTOOL_PATH b -d "
BUILD_CMD="$BUILD_CMD_START $APK_DIR $BUILD_CMD_OPTS"
APK_NAME=`echo $BUILD_CMD_OPTS | grep -Po "\-o \K.*?(?= )"`
if [ -z $APK_NAME ]; then
APK_NAME="$APK_DIR.apk"
fi
if [[ "$BUILD_CMD_OPTS" == *" -n "* || "$BUILD_CMD_OPTS" == *" -n" ]]; then
mkdir -p "$APK_DIR/res/xml"
fi
echo -e "[>] \033[1mBuilding\033[0m with $BUILD_CMD"
run "$BUILD_CMD"
echo "[>] Built!"
apk_sign "$APK_NAME"
return 0
}
apk_sign() {
APK_NAME="$1"
echo "[>] Aligning with zipalign -p 4 ...."
run "$ZIPALIGN -p 4 $APK_NAME $APK_NAME-aligned.apk"
echo "[>] Done!"
KS="$APK_SH_HOME/my-new.keystore"
if [ ! -f "$KS" ]; then
echo "[!] Keystore does not exist!"
echo "[>] Generating keystore..."
keytool -genkey -v -keystore $KS -alias alias_name -keyalg RSA -keysize 2048 -validity 10000 -storepass password -keypass password -noprompt -dname "CN=noway, OU=ID, O=Org, L=Blabla, S=Blabla, C=US"
else
echo "[>] A Keystore exist!"
fi
echo "[>] Signing $APK_NAME with apksigner..."
$APKSIGNER sign --ks $KS --ks-pass pass:password "$APK_NAME-aligned.apk"
#jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-new.keystore -storepass "password" file.apk alias_name
rm $APK_NAME
mv "$APK_NAME-aligned.apk" "$APK_NAME"
echo "[>] Done!"
echo "[>] $APK_NAME ready!"
return 0
}
apk_patch(){
# Frida gadget exposes a frida-server compatible interface, listening on localhost:27042 by default.
# run as soon as possible: frida -D emulator-5554 -n Gadget
APK_NAME=$1
ARCH=$2
GADGET_CONF_PATH=$3
BUILD_OPTS=$4
DECODE_OPTS=$5
arm=("armeabi" "armeabi-v7a")
arm64=("arm64-v8a" "arm64")
x86=("x86")
x86_64=("x86_64")
GADGET_VER=`wget https://api.github.com/repos/frida/frida/releases/latest -q -O - | grep -Po "tag_name\": \"\K.*?(?=\")"`
GADGET_ARM="frida-gadget-$GADGET_VER-android-arm.so.xz"
GADGET_ARM64="frida-gadget-$GADGET_VER-android-arm64.so.xz"
GADGET_X86_64="frida-gadget-$GADGET_VER-android-x86_64.so.xz"
GADGET_X86="frida-gadget-$GADGET_VER-android-x86.so.xz"
#GADGET_X86_64="frida-gadget-15.2.2-android-x86_64.so.xz"
# folder:arch
# 'armeabi': 'arm',
# 'armeabi-v7a': 'arm',
# 'arm64': 'arm64',
# 'arm64-v8a': 'arm64',
# 'x86': 'x86',
# 'x86_64': 'x86_64',
echo -e "[>] \033[1mPatching $APK_NAME injecting gadget for $ARCH...\033[0m"
if [[ ${ARCH} == "arm" ]]; then
GADGET=$GADGET_ARM
ARCH_DIR="armeabi-v7a"
elif [[ ${ARCH} == "x86_64" ]]; then
GADGET=$GADGET_X86_64
ARCH_DIR="x86_64"
elif [[ ${ARCH} == "x86" ]]; then
GADGET=$GADGET_X86
ARCH_DIR="x86"
elif [[ ${ARCH} == "arm64" ]]; then
GADGET=$GADGET_ARM64
ARCH_DIR="arm64-v8a"
fi
FRIDA_SO_XZ="$APK_SH_HOME/$GADGET"
FRIDA_SO="${FRIDA_SO_XZ%???}" # bash 3.x compliant xD
if [ ! -f "$FRIDA_SO" ]; then
if [ ! -f "$FRIDA_SO_XZ" ]; then
echo "[!] Frida gadget not present in $APK_SH_HOME"
echo "[>] Downloading latest frida gadget for $ARCH from github.com..."
wget https://github.com/frida/frida/releases/download/$GADGET_VER/$GADGET -q --show-progress -P $APK_SH_HOME
fi
unxz "$FRIDA_SO_XZ"
else
echo "[>] Frida gadget already present in $APK_SH_HOME"
fi
echo "[>] Using $FRIDA_SO"
APKTOOL_DECODE_OPTS=$DECODE_OPTS
apk_decode "$APK_NAME" "$APKTOOL_DECODE_OPTS"
echo -e "[>] \033[1mInjecting Frida gadget...\033[0m"
echo "[>] Placing the Frida shared object for $ARCH...."
APK_DIR=${APK_NAME%.apk} # bash 3.x compliant xD
mkdir -p "$APK_DIR/lib/$ARCH_DIR/"
cp ${FRIDA_SO_XZ::-3} $APK_DIR/lib/$ARCH_DIR/libfrida-gadget.so
if [ ! -z $GADGET_CONF_PATH ]; then
echo "[>] Placing the specified gadget configuration json file...."
cp "$GADGET_CONF_PATH" $APK_DIR/lib/$ARCH_DIR/libfrida-gadget.config.so
fi
# Inject a System.loadLibrary("frida-gadget") call into the smali,
# before any other bytecode executes or any native code is loaded.
# A suitable place is typically the static initializer of the entry point class of the app (e.g. the main application Activity).
# We have to determine the class name for the activity that is launched on application startup.
# In Objection this is done by first trying to parse the output of aapt dump badging, then falling back to manually parsing the AndroidManifest for activity-alias tags.
echo "[>] Searching for a launchable-activity..."
MAIN_ACTIVITY=`$AAPT dump badging $APK_NAME | grep launchable-activity | grep -Po "name='\K.*?(?=')"`
echo "[>] launchable-activity found --> $MAIN_ACTIVITY"
# TODO: If we dont get the activity, we gonna check out activity aliases trying to manually parse the AndroidManifest.
# Try to determine the local path for a target class' smali converting the main activity to a path
MAIN_ACTIVITY_2PATH=`echo $MAIN_ACTIVITY | tr '.' '/'`
CLASS_PATH="./$APK_DIR/smali/$MAIN_ACTIVITY_2PATH.smali"
echo "[>] Local path should be $CLASS_PATH"
# NOTE: if the class does not exist it might be a multidex setup.
# Search the class in smali_classesN directories.
CLASS_PATH_IND=1 # starts from 2
# get max number of smali_classes
CLASS_PATH_IND_MAX=$(ls -1 "./$APK_DIR" | grep "_classes[0-9]*" | wc -l)
while [ ! -f "$CLASS_PATH" ]
do
echo "[!] $CLASS_PATH does not exist! Probably a multidex APK..."
if [ $CLASS_PATH_IND -gt $CLASS_PATH_IND_MAX ]; then
# keep searching until smali_classesN then exit
echo "[>] $CLASS_PATH NOT FOUND!"
echo "[!] Can't find the launchable-activity! Sorry."
echo "[>] Bye!"
exit 1
fi
CLASS_PATH_IND=$((CLASS_PATH_IND+1))
# ./base/smali/
# ./base/smali_classes2/
CLASS_PATH="./$APK_DIR/smali_classes$CLASS_PATH_IND/$MAIN_ACTIVITY_2PATH.smali"
echo "[?] Looking in $CLASS_PATH..."
done
#
# Now, patch the smali, look for the line with the apktool's comment "# direct methods"
# Patch the smali with the appropriate loadLibrary call based on whether a constructor already exists or not.
# If an existing constructor is present, the partial_load_library will be used.
# If no constructor is present, the full_load_library will be used.
#
# Objection checks if there is an existing <clinit> to determine which is the constructor,
# then they inject a loadLibrary just before the method end.
#
# We search for *init> and inject a loadLibrary just after the .locals declaration.
#
# <init> is the (or one of the) constructor(s) for the instance, and non-static field initialization.
# <clinit> are the static initialization blocks for the class, and static field initialization.
#
echo "[>] $CLASS_PATH found!"
echo "[>] Patching smali..."
readarray -t lines < $CLASS_PATH
index=0
skip=1
for i in "${lines[@]}"
do
# partial_load_library
if [[ $i == "# direct methods" ]]; then
if [[ ${lines[$index+1]} == *"init>"* ]]; then
echo "[>>] A constructor is already present --> ${lines[$index+1]}"
echo "[>>] Injecting partial load library!"
# Skip any .locals and write after
# Do we have to skip .annotations? is ok to write before them?
if [[ ${lines[$index+2]} =~ \.locals* ]]; then
echo "[>>] .locals declaration found!"
echo "[>>] Skipping .locals line..."
skip=2
echo "[>>] Update locals count..."
locals=`echo ${lines[$index+2]} | cut -d' ' -f2`
((locals++))
lines[$index+2]=".locals $locals"
else
echo "[!!!!!!] No .locals found! :("
echo "[!!!!!!] TODO add .locals line"
fi
arr=("${lines[@]:0:$index+1+$skip}") # start of the array
# We inject a loadLibrary just after the locals declaration.
# Objection add the loadLibrary call just before the method end.
arr+=( 'const-string v0, "frida-gadget"')
arr+=( 'invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V')
arr+=( "${lines[@]:$index+1+$skip}" ) # tail of the array
lines=("${arr[@]}") # transfer back in the original array.
else
echo "[!!!!!!] No constructor found!"
echo "[!!!!!!] TODO: gonna use the full load library"
#arr+=('.method static constructor <clinit>()V')
#arr+=(' .locals 1')
#arr+=('')
#arr+=(' .prologue')
#arr+=(' const-string v0, "frida-gadget"')
#arr+=('')
#arr+=(' invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V')
#arr+=('')
#arr+=(' return-void')
#arr+=('.end method')
fi
fi
((index++))
done
echo "[>] Writing the patched smali back..."
printf "%s\n" "${lines[@]}" > $CLASS_PATH
# Add the Internet permission to the manifest if it’s not there already, to permit Frida gadget to open a socket.
echo "[?] Checking if Internet permission is present in the manifest..."
INTERNET_PERMISSION=0
MANIFEST_PATH="$APK_DIR/AndroidManifest.xml"
readarray -t manifest < $MANIFEST_PATH
for i in "${manifest[@]}"
do
if [[ "$i" == *"<uses-permission android:name=\"android.permission.INTERNET\"/>"* ]]; then
INTERNET_PERMISSION=1
echo "[>] Internet permission is there!"
break
fi
done
if [[ $INTERNET_PERMISSION == 0 ]]; then
echo "[!] Internet permission not present in the Manifest!"
echo "[>] Patching $MANIFEST_PATH"
arr=("${manifest[@]:0:1}") # start of the array
arr+=( '<uses-permission android:name="android.permission.INTERNET"/>')
arr+=( "${manifest[@]:1}" ) # tail of the array
manifest=("${arr[@]}") # transfer back in the original array.
echo "[>] Writing the patched manifest back..."
printf "%s\n" "${manifest[@]}" > $MANIFEST_PATH
fi
# Set android:extractNativeLibs="true" in the Manifest if you experience any adb: failed to install file.gadget.apk:
# Failure [INSTALL_FAILED_INVALID_APK: Failed to extract native libraries, res=-2]
echo "[>] Enabling native libraries extraction if it was set to false..."
# If the tag exist and is set to false, set it to true, otherwise do nothing
sed -i "s/android:extractNativeLibs=\"false\"/android:extractNativeLibs=\"true\"/g" $MANIFEST_PATH
echo "[>] Done!"
APKTOOL_BUILD_OPTS="-o $APK_DIR.gadget.apk --use-aapt2"
APKTOOL_BUILD_OPTS="$APKTOOL_BUILD_OPTS $BUILD_OPTS"
apk_build "$APK_DIR" "$APKTOOL_BUILD_OPTS"
echo "[>] Bye!"
return 0;
}
apk_pull(){
PACKAGE=$1
BUILD_OPTS=$2
PACKAGE_PATH=`$ADB shell pm path "$PACKAGE" | sed 's/\r//' | cut -d ":" -f 2`
if [ -z "$PACKAGE_PATH" ]; then
echo "[>] Sorry, cant find package $PACKAGE"
echo "[>] Bye!"
exit 1
fi
NUM_APK=`echo "$PACKAGE_PATH" | wc -l`
if [ $NUM_APK -gt 1 ]; then
SPLIT_DIR=$PACKAGE"_split_apks"
mkdir -p $SPLIT_DIR
echo "[>] Pulling $PACKAGE: Split apks detected!"
echo "[>] Pulling $NUM_APK apks in ./$SPLIT_DIR/"
print_ "[>] Pulling $PACKAGE from $PACKAGE_PATH<<<"
for P in $PACKAGE_PATH
do
PULL_CMD="$ADB pull $P $SPLIT_DIR"
run "$PULL_CMD"
done
# We have to combine split APKs into a single APK, for patching.
# Decode all the APKs.
echo "[>] Combining split APKs into a single APK..."
SPLIT_APKS=($SPLIT_DIR/*)
for i in "${SPLIT_APKS[@]}"
do
print_ $i
APK_NAME=$i
APK_DIR=${APK_NAME%.apk} # bash 3.x compliant xD
APKTOOL_DECODE_OPTS="--resource-mode dummy -o $APK_DIR 1>/dev/null"
apk_decode "$APK_NAME" "$APKTOOL_DECODE_OPTS"
done
# Walk the extracted APKs dirs and copy files and dirs to the base APK dir.
echo "[>] Walking extracted APKs dirs and copying files to the base APK..."
for i in "${SPLIT_APKS[@]}"
do
APK_NAME=$i
APK_DIR=${APK_NAME%.apk} # bash 3.x compliant xD
# Skip base.apk.
if [ $APK_DIR == $SPLIT_DIR"/base" ]; then
continue
fi
# Walk each apk dir.
FILES_IN_SPLIT_APK=($APK_DIR/*)
for j in "${FILES_IN_SPLIT_APK[@]}"
do
print_ "[>>>>] Parsing split apks file: "$j
# Skip Manifest, apktool.yml, and the original files dir.
if [[ $j == *AndroidManifest.xml ]] || [[ $j == *apktool.yml ]] || [[ $j == *original ]]; then
print_ "[-] Skip!"
continue
fi
# Copy files into the base APK, except for XML files in the res directory
if [[ $j == */res ]]; then
print_ "[.] /res direcorty found!":
(cd $j; find . -type f ! -name '*.xml' -exec cp --parents {} ../../base/res/ \;)# -exec echo '[+] Copying res that are not xml {}'\;)
continue
fi
print_ "[>] Copying directory cp -R $j in $SPLIT_DIR/base/ ...."
cp -R $j $SPLIT_DIR"/base/"
done
done
echo "[>] Fixing APKTOOL_DUMMY public resource identifiers..."
echo "[>] - Find all public DUMMY_NAME/REAL_NAME pairs ..."
# Fix public resource identifiers.
# Find all resource IDs with name APKTOOOL_DUMMY_xxx in the base dir
DUMMY_IDS=`grep "APKTOOL_DUMMY_" $SPLIT_DIR"/base/res/values/public.xml" | grep -Po "id=\"\K.*?(?=\")" | grep 0x`
stra=($DUMMY_IDS)
ITER=1
TOTAL=${#stra[@]}
touch $SPLIT_DIR"/DUMMY_REPLACEMENT.txt"
for j in "${stra[@]}"
do
print_ "[~] ("$ITER"/"$TOTAL") DUMMY_ID_TO_FIX: "$j
# Get the dummy name grepping for the resource ID
DUMMY_NAME=`grep "$j" $SPLIT_DIR/base/res/values/public.xml | grep DUMMY | grep -Po "name=\"\K.*?(?=\")"`
print_ "[~] ("$ITER"/"$TOTAL") DUMMY_NAME: "$DUMMY_NAME
# Get the real resource name grepping for the resource ID in each spit APK
REAL_NAME=`grep "$j" $SPLIT_DIR/*/res/values/public.xml | grep -v DUMMY | grep -v base | grep name | grep -Po "name=\"\K.*?(?=\")"`
print_ "[~] ("$ITER"/"$TOTAL") REAL_NAME: "$REAL_NAME
echo "s/\<$DUMMY_NAME\>/$REAL_NAME/g" >> $SPLIT_DIR"/DUMMY_REPLACEMENT.txt"
print_ "---"
ITER=$(expr $ITER + 1)
done
echo "[>] - Replace DUMMY_NAME/REAL_NAME in all base.apk xml files containing APKTOOL_DUMMY_"
grep -rl "APKTOOL_DUMMY_" --include "*\.xml" $SPLIT_DIR"/base" | xargs sed -i -f $SPLIT_DIR"/DUMMY_REPLACEMENT.txt"
rm $SPLIT_DIR"/DUMMY_REPLACEMENT.txt"
echo "[>] Done!"
# Disable APK splitting in the base manifest file, if it’s not there already done.
MANIFEST_PATH="$SPLIT_DIR/base/AndroidManifest.xml"
echo "[>] Disabling APK splitting :"
echo "[>] - Make sure isSplitRequired is set to false"
sed -i "s/android:isSplitRequired=\"true\"/android:isSplitRequired=\"false\"/g" $MANIFEST_PATH
echo "[>] - Make sure com.android.vending.splits.required is set to false"
sed -i "/com.android.vending.splits.required/s/true/false/g" $MANIFEST_PATH
echo "[>] Done!"
# Set android:extractNativeLibs="true" in the Manifest if you experience any adb: failed to install file.gadget.apk:
# Failure [INSTALL_FAILED_INVALID_APK: Failed to extract native libraries, res=-2]
echo "[>] Enabling native libraries extraction if it was set to false..."
# If the tag exist and is set to false, set it to true, otherwise do nothing
sed -i "s/android:extractNativeLibs=\"false\"/android:extractNativeLibs=\"true\"/g" $MANIFEST_PATH
echo "[>] Done!"
# Rebuild the base APK
APKTOOL_BUILD_OPTS="-o file.single.apk --use-aapt2"
APKTOOL_BUILD_OPTS="$APKTOOL_BUILD_OPTS $BUILD_OPTS"
apk_build "$SPLIT_DIR/base" "$APKTOOL_BUILD_OPTS"
echo "[>] Bye!"
return 0
else
echo "[>] Pulling $PACKAGE from $PACKAGE_PATH"
PULL_CMD="$ADB pull $PACKAGE_PATH ."
run "$PULL_CMD"
echo "[>] Done!"
echo "[>] Bye!"
return 0
fi
}
apk_rename(){
APK_NAME=$1
PACKAGE=$2
BUILD_OPTS=$3
echo -e "[>] \033[1mRenaming $APK_NAME\033[0m to $PACKAGE"
APKTOOL_DECODE_OPTS="1>/dev/null"
apk_decode "$APK_NAME" "$APKTOOL_DECODE_OPTS"
APK_DIR=${APK_NAME%.apk} # bash 3.x compliant xD
APKTOOL_YML_PATH="$APK_DIR/apktool.yml"
echo "[>] Updating renameManifestPackage in apktool.yml with $PACKAGE"
# Note: https://github.com/iBotPeaches/Apktool/issues/1753
# renameManifestPackage is not designed for manual package name changes, but can be useful in some situations.
sed -i "s/renameManifestPackage:.*/renameManifestPackage: $PACKAGE/g" $APKTOOL_YML_PATH
APKTOOL_BUILD_OPTS="-o file.renamed.apk --use-aapt2"
APKTOOL_BUILD_OPTS="$APKTOOL_BUILD_OPTS $BUILD_OPTS"
# Silently build
apk_build "$APK_DIR" "$APKTOOL_BUILD_OPTS 1>/dev/null"
return 0;
}
#####################################################################
#####################################################################
check_apk_tools
if [ ! -z $1 ]&&[ $1 == "build" ]; then
if [ -z "$2" ]; then
echo "Pass the apk directory name!"
echo "./apk build <apk_dir>"
exit 1
fi
#
# It seems there is a problem with apktool build and manifest attribute android:dataExtractionRules
# : /home/ax/AndroidManifest.xml:30: error: attribute android:dataExtractionRules not found.
# W: error: failed processing manifest.
# Temporary workaround: remove the attribute from the Manifest and use Android 9
#
# Set android:extractNativeLibs="true" in the Manifest if you experience any adb:
# failed to install file.gadget.apk: Failure [INSTALL_FAILED_INVALID_APK: Failed to extract native libraries, res=-2]
# https://github.com/iBotPeaches/Apktool/issues/1626 - zipalign -p 4 seems to not resolve the issue.
#
APK_DIR=$2
exit_if_not_exist "$APK_DIR"
APKTOOL_BUILD_OPTS="-o file.apk --use-aapt2"
#APKTOOL_BUILD_OPTS="--use-aapt2"
shift # pop SUBCOMMAND
shift # pop SUBCOMMAND_ARG
while [[ $# -gt 0 ]]; do
case $1 in
-n|--net)
APKTOOL_BUILD_OPTS="$APKTOOL_BUILD_OPTS -n"
shift # arg
;;
-*|--*)
echo "[!] Unknown option $1"
exit 1
;;
*)
POSITIONAL_ARGS+=("$1") # save positional arg
shift # arg
;;
esac
done
apk_build "$APK_DIR" "$APKTOOL_BUILD_OPTS"
exit 0
elif [ ! -z $1 ]&&[ $1 == "decode" ]; then
if [ -z "$2" ]; then
echo "Pass the apk name!"
echo "./apk decode <apkname.apk>"
exit 1
fi
APK_NAME=$2
exit_if_not_exist "$APK_NAME"
APKTOOL_DECODE_OPTS=""
shift # pop SUBCOMMAND
shift # pop SUBCOMMAND_ARG
while [[ $# -gt 0 ]]; do
case $1 in
-s|--safe)
APKTOOL_DECODE_OPTS="$APKTOOL_DECODE_OPTS -r" # no decode res
shift # arg
;;
-d|--no-dis)
APKTOOL_DECODE_OPTS="$APKTOOL_DECODE_OPTS -s" # no disass dex
shift # argument
;;
-*|--*)
echo "[!] Unknown option $1"
exit 1
;;
*)
POSITIONAL_ARGS+=("$1") # save positional arg
shift # arg
;;
esac
done
apk_decode "$APK_NAME" "$APKTOOL_DECODE_OPTS"
exit 0
elif [ ! -z $1 ]&&[ $1 == "patch" ]; then
if [ -z "$2" ]; then
echo "Pass the apk name and the arch param!"
echo "./apk patch <apkname.apk> --arch arm"
echo "[>] Bye!"
exit 1
fi
APK_NAME=$2
APKTOOL_BUILD_OPTS=""
GADGET_CONF_PATH=""
APKTOOL_DECODE_OPTS=""
exit_if_not_exist "$APK_NAME"
shift # pop SUBCOMMAND
shift # pop SUBCOMMAND_ARG
while [[ $# -gt 0 ]]; do
case $1 in
-a|--arch)
# what if $2 not exist
ARCH="$2"
shift # arg
shift # val
;;
-g|--gadget-conf)
GADGET_CONF_PATH="$2"
exit_if_not_exist "$GADGET_CONF_PATH"
shift # argument
shift # value
;;
-n|--net)
APKTOOL_BUILD_OPTS="$APKTOOL_BUILD_OPTS -n"
shift # argument
;;
--only-main-classes)
APKTOOL_DECODE_OPTS="$APKTOOL_DECODE_OPTS --only-main-classes"
shift # argument
;;
-*|--*)
echo "[!] Unknown option $1"
exit 1
;;
*)
POSITIONAL_ARGS+=("$1") # save positional arg
shift # argument
;;
esac
done
if [ -z "$ARCH" ]; then
echo "[!] Pass the --arch param with a supported arch"
echo "./apk patch <apkname.apk> --arch arm"
echo "[>] Bye!"
exit 1
fi
if [[ ! "${supported_arch[*]}" =~ "${ARCH}" ]]; then
echo "[!] Architecture not supported!"
echo "[>] Bye!"
exit 1
fi
apk_patch "$APK_NAME" "$ARCH" "$GADGET_CONF_PATH" "$APKTOOL_BUILD_OPTS" "$APKTOOL_DECODE_OPTS"
exit 0
elif [ ! -z $1 ]&&[ $1 == "pull" ]; then
if [ -z "$2" ]; then
echo "Pass the package name!"
echo "./apk pull <com.package.name>"
exit 1
fi
PACKAGE_NAME=$2
APKTOOL_BUILD_OPTS=""
if [ ! -z "$3" ]&&[ "$3" == "--net" ]; then
APKTOOL_BUILD_OPTS="$APKTOOL_BUILD_OPTS -n"
fi
apk_pull "$PACKAGE_NAME" "$APKTOOL_BUILD_OPTS"
elif [ ! -z $1 ]&&[ $1 == "rename" ]; then
if [ -z "$2" ]; then
echo "Pass the apk name!"
echo "./apk rename <apkname.apk> <com.package.name>"
exit 1
fi
APK_NAME=$2
exit_if_not_exist "$APK_NAME"
if [ -z "$3" ]; then
echo "Pass the package name"
echo "./apk rename <apkname.apk> <com.package.name>"
echo "[>] Bye!"
exit 1
fi
PACKAGE_NAME=$3
APKTOOL_BUILD_OPTS=""
if [ ! -z "$4" ]&&[ "$4" == "--net" ]; then
APKTOOL_BUILD_OPTS="$APKTOOL_BUILD_OPTS -n"
fi
apk_rename "$APK_NAME" "$PACKAGE_NAME" "$APKTOOL_BUILD_OPTS"
exit 0
elif [ ! -z $1 ]&&[ $1 == "sign" ]; then
if [ -z "$2" ]; then
echo "Pass the apk name!"
echo "./apk sign <apkname.apk>"
echo "[>] Bye!"
exit 1
fi
APK_NAME=$2
exit_if_not_exist "$APK_NAME"
apk_sign "$APK_NAME"
exit 0
else
echo "[!] First arg must be build, decode, pull, rename or patch!"
echo " ./apk.sh pull <package_name>"
echo " ./apk.sh decode <apk_file>"
echo " ./apk.sh build <apk_dir>"
echo " ./apk.sh patch <apk_file> --arch arm"
echo " ./apk.sh rename <apk_file> <package_name>"
echo " ./apk.sh sign <apk_file>"
echo "[>] Bye!"
exit 1
fi