#!/bin/bash
#
# apk.sh v1.1
# 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.
#
#	-r, --no-res		Do not decode resources, optional. (i.e. apktool -r).
#				Can be used when decoding and when patching.
#
#	-s, --no-src		Do not disassemble dex, optional. (i.e. apktool -s).
#				Can be used when decoding and when patching.
#
#	--only-main-classes	Only disassemble the main dex classes, optional when decoding (i.e. apktool d --only-main-classes).
#
# -----------------------------------------------------------------------------


VERSION="1.1"
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"
# try local apktool without accessing internet

DEXPATCH_PATH="$APK_SH_HOME/dexpatch-0.1.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"

check_dexpatch(){
	if [ -f "$DEXPATCH_PATH" ]; then
		echo "[*] DEXpatch v0.1 exist in $APK_SH_HOME"
    else
        DEXPATCH_DOWNLOAD_URL="https://github.com/ax/DEXPatch/releases/download/v0.1/dexpatch-0.1.jar"
        echo "[!] No DEXpatch found!"
		echo "[>] Downloading DEXpatch from $DEXPATCH_DOWNLOAD_URL"
		wget $DEXPATCH_DOWNLOAD_URL -q --show-progress -P $APK_SH_HOME 
    fi
}

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..."
    echo " with $APKSIGNER sign --ks $KS --ks-pass pass:password $APK_NAME-aligned.apk"
	run "$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
	NO_DIS=$6
	NO_RES=$7

    print_ "NO DIS: $NO_DIS"
    print_ "NO RES: $NO_RES"

	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 '.' '/'`
    
    if [[ $NO_DIS == 1 ]]; then
        echo "[>] Patching with direct bytecode manipulation (no disassembly)..."
        echo "[>] Searching the dex with the Activity $MAIN_ACTIVITY..."
        MAIN_ACTIVITY_DEX=`grep -lr "$MAIN_ACTIVITY;" ./$APK_DIR/classes*.dex`
        if [ -f "$MAIN_ACTIVITY_DEX" ]; then

            echo "[>] $MAIN_ACTIVITY found in $MAIN_ACTIVITY_DEX"
            echo "[>] Patching the dex..."
            echo "[>] Running java -jar $DEXPATCH_PATH $MAIN_ACTIVITY_DEX $MAIN_ACTIVITY_DEX.gadget $MAIN_ACTIVITY_2PATH"
            run "java -jar $DEXPATCH_PATH $MAIN_ACTIVITY_DEX $MAIN_ACTIVITY_DEX.gadget $MAIN_ACTIVITY_2PATH"
            echo "[>] Renaming to $MAIN_ACTIVITY_DEX..."
            mv $MAIN_ACTIVITY_DEX.gadget $MAIN_ACTIVITY_DEX
        else
            echo "[!] Can't find the dex with $MAIN_ACTIVITY! Sorry."
            echo "[>] Bye!"
            exit 1
        fi
    else
        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
        # 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.
        #
        # Checks if there is an existing <clinit> to determine which is the class initializer block,
        # executed once when the class loaded into memory,
        # then they inject a loadLibrary just after the .locals declaration
        #
        echo "[>] $CLASS_PATH found!"
        echo "[>] Patching smali..."
        readarray -t lines < $CLASS_PATH
        index=0
        skip=1
        STATIC_CONSTRUCTOR_FOUND=0;
        for i in "${lines[@]}"
        do
            # partial_load_library
            if [[ $i == ".method static constructor <clinit>"* ]]; then
                STATIC_CONSTRUCTOR_FOUND=1;
                echo "[>>] A constructor is already present --> ${lines[$index]}"
                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+1]} =~ \.locals* ]]; then
                    echo "[>>] .locals declaration found!"
                    echo "[>>] Skipping .locals line..."
                    skip=2
                    echo "[>>] Update locals count..."
                    # The .locals directive specifies how many registers are allocated for the method.
                    # If the original method uses v0 and v1, our injected code uses v0, then the total should still be 2.
                    # So the local++ is probably wrong here. Anyway it should not affect VM execution, it should be just metadata. 
                    locals=`echo ${lines[$index+1]} | cut -d' ' -f2`
                    ((locals++))
                    lines[$index+1]=".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.
            fi
            ((index++))
        done
        if [[ $STATIC_CONSTRUCTOR_FOUND == 0  ]]; then
            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
        echo "[>] Writing the patched smali back..."
        printf "%s\n" "${lines[@]}" > $CLASS_PATH

        if [ "$NO_RES" -ne 1 ]; then
            # 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!"
        fi #end NO_RES
    fi #end NO_DIS if

	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 
check_dexpatch

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
			-r|--no-res)
		  		APKTOOL_DECODE_OPTS="$APKTOOL_DECODE_OPTS -r" # no decode res
				shift # arg
		  	;;
			-s|--no-src)
		  		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=""
    NO_DIS=0
	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
		  	;;		
			-r|--no-res)
		  		APKTOOL_DECODE_OPTS="$APKTOOL_DECODE_OPTS -r"
                NO_RES=1
		  		shift # argument
		  	;;
			-s|--no-src)
		  		APKTOOL_DECODE_OPTS="$APKTOOL_DECODE_OPTS -s"
                NO_DIS=1
		  		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" "$NO_DIS" "$NO_RES"
	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
