├── asla_in_action.png ├── finder_network.png ├── LICENSE ├── README.md └── asla.sh /asla_in_action.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/giuseppetotaro/asla/HEAD/asla_in_action.png -------------------------------------------------------------------------------- /finder_network.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/giuseppetotaro/asla/HEAD/finder_network.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Giuseppe Totaro 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Apple Silicon Logical Acquisition Script 2 | 3 | The Apple Silicon Logical Acquisition (ASLA) script in this repository is a bash script called `asla.sh` designed for performing logical acquisition of Apple Silicon Mac devices. 4 | This script facilitates the collection of data from an Apple Silicon Mac in a forensically-sound manner. 5 | 6 | The use of ASLA is further documented with more examples in [this blog post](https://giuseppetotaro.github.io/mac/forensics/2024/02/23/forensic-logical-acquisition-of-apple-silicon.html). 7 | 8 | ## Description 9 | 10 | To transfer files from an Apple Silicon Mac device to another Mac, you can leverage the _share disk mode_, which turns the system into an [SMB file sharing server](https://www.macrumors.com/how-to/transfer-files-apple-silicon-mac-to-mac/), providing another Mac device connected to it with file-level access to user data. 11 | Therefore, it is not possible creating a proper forensic image of an Apple Silicon Mac, but only performing a logical acquisition of the shared disk, assuming that the password is not needed or is known by the examiner, by relying on a command-line tool which copies the contents from source to destination. 12 | 13 | The ASLA script is a bash script designed to facilitate the forensically-sound logical acquisition of data from Apple Silicon Mac devices. 14 | ASLA is a simple bash script, structured in functions, written with a focus on maintaining read-only access to data, which provides a streamlined solution for collecting data from Apple Silicon Macs. 15 | With interactive prompts and detailed logging, ASLA ensures a reliable and transparent acquisition process suitable for forensic activities and data analysis. 16 | It also provides the possibility to run in _assisted mode_, to help you in identifying the targeted Apple Silicon Mac started in share disk mode and connected to your Mac device used for the acquisition. 17 | 18 | ### Getting started 19 | 1. Download the `asla.sh` script from this repository or clone it to your Mac device. 20 | 2. Ensure that the script has executable permissions: 21 | ``` 22 | % git clone https://github.com/giuseppetotaro/asla 23 | % cd asla 24 | % chmod +x ./asla.sh 25 | ``` 26 | 3. Run the script: 27 | ``` 28 | % ./asla.sh /path/to/target /path/to/destination 29 | ``` 30 | 4. Follow on-screen instructions if executed in assisted mode, which will guide you through the acquisition process. 31 | 5. Once the acquisition process is complete, the script will provide a summary of the operation and the location of the acquired data. 32 | 33 | ### Procedure 34 | 35 | Sharing the disk of an Apple Silicon Mac device (i.e., share disk mode) is a crucial step in performing logical acquisitions. While the procedure is officially [documented by Apple](https://support.apple.com/guide/mac-help/transfer-files-a-mac-apple-silicon-mchlb37e8ca7/mac), based on practical experience, there are certain nuances and tricks that can streamline the process and mitigate potential challenges. 36 | This section will provide comprehensive instructions to effectively enable share disk mode on Apple Silicon Mac devices. 37 | By following these steps, you can confidently initiate the share disk mode and proceed with the logical acquisition: 38 | 39 | * _Host_: the Mac device used for the acquisition, where the script is executed. 40 | * _Target_: the Mac device to be acquired, started in share disk mode. 41 | 42 | 1. The target must be started in Recovery Mode (press and hold power button). 43 | 2. Selecting _Options_ opens macOS Recovery. 44 | 3. If requested, select a user and enter the password for. 45 | 4. Once Recovery Mode starts, from the menu on the top, select _Utilities_ > _Share Disk_ to start sharing. 46 | 5. Select the disk that you want to share. If the disk is locked with File Vault, it must be unlocked by entering the password. 47 | 6. Share the disk by clicking _Start Sharing_. 48 | 7. Ensure the host is powered on and connected to power charger, before connecting to the target. Power supply to the target should be provided only after connecting to the host (it has been experienced that an Apple Silicon Mac is not seen if it is already connected to the power supply). 49 | 8. Connect from a USB-C port on the target to a USB-C port on the host via USB or [Thunderbolt 3 (TB3) cable](https://support.apple.com/en-us/111750). The host should prompt to [allow accessory to connect](https://support.apple.com/guide/mac-help/allow-accessories-to-connect-mchlf779ae93/mac) (the [setting](https://support.apple.com/en-us/102282) for allowing accessories to connect can be easily changed). 50 | 9. Once connected, you should hear a sound which means that (most probably) the target is connected to the host. 51 | 10. Connect the target with power supply. 52 | 11. With Finder, select _Go_ > _Network_ (or press SHIFT+CMD+K) and check if you see an icon with the target's name (e.g., MacBook Air), as you can see below. 53 | 54 | ![Finder Network](finder_network.png) 55 | 56 | To ensure the shared disk will be mounted in read-only mode, users can utilize the `asla.sh` in assisted mode (`-a` option), which indeed helps in identifying the target and automatically mounts the shared disk in read-only mode: 57 | 58 | ``` 59 | ./asla.sh -a TARGET DESTINATION 60 | ``` 61 | 62 | or 63 | 64 | ``` 65 | ./asla.sh -a -n "MacBook Air" -u username -p password TARGET DESTINATION 66 | ``` 67 | 68 | or 69 | 70 | ``` 71 | ./asla.sh -a -n "MacBook Air" -u Guest --no-password TARGET DESTINATION 72 | ``` 73 | 74 | where `TARGET` and `DESTINATION` are the paths to the mount point of the target's shared disk and the location (preferably external) where the acquisition will be saved respectively. 75 | 76 | ![The script in action](asla_in_action.png) 77 | 78 | ## Installation 79 | 80 | This script can be executed in Terminal and would not need any specific software besides those already provided in macOS. 81 | However, it is highly recommended that you have the [Xcode Command Line Tools](https://developer.apple.com/xcode/) installed on your system. 82 | 83 | To install the Xcode Command Line Tools, you can enter the command `xcode-select --install` in the Terminal. 84 | 85 | This script has been tested on macOS Sonoma (Version 14.3) with the Xcode Command Line Tools installed. 86 | 87 | ## Usage 88 | 89 | The `asla.sh` script offers a concise but effective help message using the `-h` option as follows: 90 | 91 | `% ./asla.sh -h` 92 | 93 | ``` 94 | ASLA (Apple Silicon Logical Acquisition) version 1.0 95 | Copyright (c) 2024 Giuseppe Totaro 96 | GitHub repo: https://github.com/giuseppetotaro/asla 97 | 98 | asla.sh is provided "as is", WITHOUT WARRANTY OF ANY KIND. You are welcome to 99 | redistribute it under certain conditions. See the MIT Licence for details. 100 | 101 | asla.sh is a bash script to perform the logical acquisition of data from the 102 | targeted Apple Silicon Mac started in "share disk mode". 103 | 104 | Usage: ./asla.sh [OPTION]... TARGET DESTINATION 105 | 106 | TARGET path to the target (i.e., the mount point of the Mac's shared disk 107 | to be acquired). 108 | DESTINATION path to the folder where the sparse image used as destination will 109 | be created. 110 | 111 | If the target is a path to a non-existing folder, the script will run in 112 | assisted mode (equivalent to using the -a option) to identify the target. 113 | 114 | Examples: 115 | ./asla.sh /Volumes/ShareDisk /Volumes/ExternalDrive 116 | ./asla.sh -a -c /tmp/target /Volumes/ExternalDrive 117 | ./asla.sh -n "MacBook Air" -u user -p password /tmp/target /Volumes/Dest 118 | ./asla.sh -i MyAcquisition -s 500 /Volumes/ShareDisk /Volumes/Dest 119 | ./asla.sh -t rsync /Volumes/ShareDisk /Volumes/ExternalDrive 120 | 121 | Options: 122 | -h, --help print this help message 123 | -a, --assisted run the script in assisted mode 124 | -c, --calculate-hash calculate MD5 and SHA1 hashes of the sparse image 125 | -i, --image-name name of the sparse image (without extension) 126 | -n, --name computer name of the target (only in assisted mode) 127 | --no-password no password will be used (only in assisted mode) 128 | -p, --password password of the target (only in assisted mode) 129 | -s, --size size of the sparse image in KB, otherwise it will 130 | be calculated based on the size of the target 131 | -t, --tool tool for the acquisition (cp is the default) 132 | -u, --user username of the target (only in assisted mode) 133 | ``` 134 | 135 | ## Contributing 136 | 137 | Contributions to this project are welcome! If you encounter any issues, have suggestions for improvements, or would like to contribute new features, please feel free to submit a pull request or open an issue on GitHub. 138 | 139 | Please try to structure your code into functions. If you want to add a new function to the script, please consider that any function that is neither obvious nor short must be commented, using the function comments suggested in the [Shell Style Guide](https://google.github.io/styleguide/shellguide.html#s4-comments). 140 | 141 | ## Authors and Acknowledgments 142 | 143 | This script was developed by Giuseppe Totaro based on extensive experience in the field of digital forensics. 144 | 145 | Special thanks to the following colleagues for their invaluable insights, feedback, and testing contributions: 146 | 147 | - Israel Gordillo Torres 148 | - Francesco Cappotto 149 | - Sammy Nieuwborg 150 | 151 | Their expertise and dedication greatly enhanced the quality and reliability of this script. 152 | 153 | ## License 154 | 155 | This project is licensed under the [MIT License](LICENSE). Feel free to modify and distribute the script according to the terms of this license. 156 | 157 | # Roadmap 158 | 159 | - [ ] Option to convert sparse image to dmg 160 | - [ ] Option to automatically unmount the target at the end -------------------------------------------------------------------------------- /asla.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Script : asla.sh 4 | # Usage : ./asla.sh /path/tp/target /path/to/destination 5 | # Author : Giuseppe Totaro 6 | # Date : 2024-02-01 7 | # Last Edited: 2024-02-26 8 | # Description: This script performs the logical acquisition of data from the 9 | # target (i.e., the Apple Silicon Mac to be acquired) started in 10 | # "share disk mode", by leveraging either "cp" or "rsync" on the 11 | # host (i.e., the Mac device of the forensic examiner). 12 | # Basically, the script performs the following actions: 13 | # 1. If executed in assisted mode, it mounts read-only on the host 14 | # the shared disk of the target. 15 | # 2. On the host, it creates a sparse image which is mounted to be 16 | # used as the destination of the acquisition. 17 | # 3. It leverages a copy tool (i.e., "cp" or "rsync") to copy 18 | # the data from the target to the attached sparse image on the 19 | # host, preserving the original file attributes. 20 | # 4. It detaches the sparse image and generates the log files of 21 | # the acquisition process. 22 | # This script is released under the MIT License (MIT). 23 | # Notes : The target must be started in "share disk mode". 24 | # 25 | 26 | #set -o errexit 27 | #set -o pipefail 28 | #set -o nounset 29 | 30 | # Global Variables 31 | 32 | VERSION="1.0" 33 | REPO="https://github.com/giuseppetotaro/asla" 34 | TOOLS=("cp" "rsync") 35 | OUT_FILE= 36 | LOG_FILE= 37 | ERR_FILE= 38 | VOLUME_NAME= 39 | 40 | # Functions 41 | 42 | ####################################### 43 | # Clean up detaching the attached sparse image. 44 | # Globals: 45 | # VOLUME_NAME 46 | # Arguments: 47 | # None 48 | # Outputs: 49 | # Writes error message to stdout. 50 | ####################################### 51 | cleanup() { 52 | echo "# An error occurred. Cleaning up..." 53 | [[ -d "${VOLUME_NAME}" ]] && detach_image "${VOLUME_NAME}" 54 | echo "# Process has been terminated with errors." 55 | echo "# Check manually if target has been mounted anyway and, if so, unmount it." 56 | } 57 | 58 | ####################################### 59 | # Print help message. 60 | # Arguments: 61 | # None 62 | # Outputs: 63 | # Writes the help message to stdout. 64 | ####################################### 65 | print_usage() { 66 | cat << EOF 67 | ASLA (Apple Silicon Logical Acquisition) version $VERSION 68 | Copyright (c) 2024 Giuseppe Totaro 69 | GitHub repo: https://github.com/giuseppetotaro/asla 70 | 71 | asla.sh is provided "as is", WITHOUT WARRANTY OF ANY KIND. You are welcome to 72 | redistribute it under certain conditions. See the MIT Licence for details. 73 | 74 | asla.sh is a bash script to perform the logical acquisition of data from the 75 | targeted Apple Silicon Mac started in "share disk mode". 76 | 77 | Usage: ${0} [OPTION]... TARGET DESTINATION 78 | 79 | TARGET path to the target (i.e., the mount point of the Mac's shared disk 80 | to be acquired). 81 | DESTINATION path to the folder where the sparse image used as destination will 82 | be created. 83 | 84 | If the target is a path to a non-existing folder, the script will run in 85 | assisted mode (equivalent to using the -a option) to identify the target. 86 | 87 | Examples: 88 | ./asla.sh /Volumes/ShareDisk /Volumes/ExternalDrive 89 | ./asla.sh -a -c /tmp/target /Volumes/ExternalDrive 90 | ./asla.sh -n "MacBook Air" -u user -p password /tmp/target /Volumes/Dest 91 | ./asla.sh -i MyAcquisition -s 500 /Volumes/ShareDisk /Volumes/Dest 92 | ./asla.sh -t rsync /Volumes/ShareDisk /Volumes/ExternalDrive 93 | 94 | Options: 95 | -h, --help print this help message 96 | -a, --assisted run the script in assisted mode 97 | -c, --calculate-hash calculate MD5 and SHA1 hashes of the sparse image 98 | -i, --image-name name of the sparse image (without extension) 99 | -n, --name computer name of the target (only in assisted mode) 100 | --no-password no password will be used (only in assisted mode) 101 | -p, --password password of the target (only in assisted mode) 102 | -s, --size size of the sparse image in KB, otherwise it will 103 | be calculated based on the size of the target 104 | -t, --tool tool for the acquisition (cp is the default) 105 | -u, --user username of the target (only in assisted mode) 106 | EOF 107 | } 108 | 109 | ####################################### 110 | # Backup the existing sparse image and log files to a specific folder named as 111 | # the current date and time. 112 | # Globals: 113 | # OUT_FILE 114 | # LOG_FILE 115 | # ERR_FILE 116 | # Arguments: 117 | # destination path, the folder where the backup will be created. 118 | # image_name, the name of the sparse image without extension. 119 | # Outputs: 120 | # Writes the backup folder and files to stdout. 121 | ####################################### 122 | backup() { 123 | local destination="${1}" 124 | local image_name="${2}" 125 | local files=() 126 | files+=("${destination}/${image_name}.sparseimage") 127 | files+=("${OUT_FILE}") 128 | files+=("${LOG_FILE}") 129 | files+=("${ERR_FILE}") 130 | local now=$(date +'%Y%m%d%H%M%S') 131 | mkdir -p "${destination}" 132 | for file in "${files[@]}" 133 | do 134 | if [[ -f "${file}" ]] 135 | then 136 | mkdir "${destination}/${now}" 2>/dev/null && printf "# Created backup folder %s\n" "${destination}/${now}" 137 | fname=$(basename "${file}") 138 | mv "${file}" "${destination}/${now}/${now}.${fname}" 2>/dev/null 139 | printf "# Backed up %s to %s\n" "${file}" "${destination}/${now}" 140 | fi 141 | done 142 | } 143 | 144 | #TODO: print_instructions to acquire a Mac with Apple Silicon 145 | #print_instructions() { 146 | #} 147 | 148 | ####################################### 149 | # Normalize a string by replacing spaces with %20. 150 | # Arguments: 151 | # name, the string to be normalized. 152 | ####################################### 153 | normalize_name() { 154 | local name="${1}" 155 | echo "${name}" | sed 's/ /%20/g' 156 | } 157 | 158 | ####################################### 159 | # Mount the shared disk of the target at the given mount point. 160 | # Arguments: 161 | # target_name, the name of the target computer. 162 | # target_user, the username of the target computer. 163 | # target_pass, the password of the target computer. 164 | # mount_point, the mount point of the shared disk. 165 | # Outputs: 166 | # Writes info about the shared disk to stdout. 167 | ####################################### 168 | mount_shared_disk() { 169 | local target_name=$(normalize_name "${1}") 170 | local target_user="${2}" 171 | local target_pass="${3}" 172 | local mount_point="${4}" 173 | local mount_pass= 174 | [[ -z "${target_pass}" ]] && mount_pass="" || mount_pass=":${target_pass}" 175 | local host="//${target_user}${mount_pass}@${target_name}._smb._tcp.local" 176 | printf "# Attempting to list resources on %s (password of target might be required) ...\n" "${host}" 177 | res=$(smbutil view ${host}) 178 | local shared_disk=$(echo "${res}" | sed -rn 's/(.+[^[:space:]])[[:space:]]+Disk.*/\1/p') 179 | printf "# Found shared disk %s. Creating mount point at %s ...\n" "${shared_disk}" "${mount_point}" 180 | mkdir -p "${mount_point}" 181 | norm_shared_disk=$(normalize_name "${shared_disk}") 182 | printf "# Mounting %s/%s at %s ...\n" "${host}" "${norm_shared_disk}" "${mount_point}" 183 | mount_smbfs -o ro "${host}/${norm_shared_disk}" "${mount_point}" 184 | } 185 | 186 | ####################################### 187 | # Print the acquisition info. 188 | # Arguments: 189 | # target, the target folder. 190 | # destination, the destination folder. 191 | # image_name, the name of the sparse image. 192 | # tool, the tool used to copy data from target. 193 | # Outputs: 194 | # Writes the acquisition info to stdout. 195 | ####################################### 196 | print_acquisition_info() { 197 | local target="${1}" 198 | local destination="${2}" 199 | local image_name="${3}" 200 | local tool="${4}" 201 | local target_space=$(df -h "${target}") 202 | cat << EOF 203 | # Process started at ${start_datetime} 204 | 205 | # Acquisition Info 206 | # ---------------- 207 | # Target: ${target} 208 | # Destination: ${destination} 209 | # Image Name: ${image_name}.sparseimage 210 | # Tool: ${tool} 211 | 212 | # Displaying the target free disk space... 213 | $target_space 214 | 215 | EOF 216 | } 217 | 218 | ####################################### 219 | # Create a sparse image and attach it to the host. 220 | # Globals: 221 | # VOLUME_NAME 222 | # Arguments: 223 | # image_size, the size of the sparse image in KiloBytes. 224 | # destination, the destination folder where the sparse image will be created. 225 | # image_name, the name of the sparse image. 226 | # target, the target folder 227 | # Outputs: 228 | # Writes the backup folder and files to stdout. 229 | ####################################### 230 | create_sparse_image() { 231 | local image_size=${1} 232 | local destination="${2}" 233 | local image_name="${3}" # To be used as volume name 234 | local target="${4}" 235 | [[ -z "${image_size}" ]] && image_size=$(df -k "${target}" | tail -1 | awk '{print (substr($2,1,1)+1)*(10^(length($2)-1))}') 236 | 237 | cat << EOF 238 | # Sparse Image 239 | # ------------ 240 | # Creating and attaching the sparse image of size ${image_size}k... 241 | EOF 242 | 243 | mkdir -p "${destination}" 244 | destination_fullpath="${destination}/${image_name}" 245 | out=$(hdiutil create -size ${image_size}k -volname "${image_name}" -fs APFS -layout GPTSPUD -type SPARSE -attach "${destination_fullpath}") 246 | 247 | cat << EOF 248 | # Sparse image created at ${destination_fullpath}. Output: 249 | ${out} 250 | 251 | EOF 252 | 253 | # Commentary: The volume name should be the image name unless a volume with 254 | # the same name already exists. This is to get the actual volume name from the 255 | # output of the hdiutil command. 256 | VOLUME_NAME=$(echo "${out}" | sed -rn 's/.+(\/Volumes\/.+[^[:space:]]).*/\1/p' | head -1) 257 | } 258 | 259 | ####################################### 260 | # Acquire data from the target to the attached sparse image on the host. 261 | # Globals: 262 | # LOG_FILE 263 | # ERR_FILE 264 | # Arguments: 265 | # target, the target folder. 266 | # volume_name, the volume name of the attached sparse image. 267 | # tool, the tool used to copy data from target (cp or rsync). 268 | # Outputs: 269 | # Writes the paths to target and attached volume to stdout, copied files to 270 | # LOG_FILE, and errors to ERR_FILE. 271 | ####################################### 272 | acquire_data() { 273 | local target=$(echo "${1}" | sed -e 's#[^/]$#&/#') 274 | local volume_name=$(echo "${2}" | sed -e 's#[^/]$#&/#') 275 | local tool="${3}" 276 | 277 | cat << EOF 278 | # Data Acquisition 279 | # ---------------- 280 | # Acquiring data from ${target} to ${volume_name}... 281 | EOF 282 | if [[ "${tool}" == "cp" ]] 283 | then 284 | cp -PRpvi "${target}." "${volume_name}" > "${LOG_FILE}" 2> "${ERR_FILE}" && rc=$? || rc=$? 285 | elif [[ "${tool}" == "rsync" ]] 286 | then 287 | rsync -artvqX --log-file="${LOG_FILE}" "${target}" "${volume_name}" > "${LOG_FILE}" 2> "${ERR_FILE}" && rc=$? || rc=$? 288 | fi 289 | 290 | if [[ ${rc} -eq 0 ]] 291 | then 292 | printf "# Data from %s copied to %s with %s completed successfully\n\n" "${target}" "${volume_name}" "${tool}" 293 | else 294 | printf "# Data from %s copied to %s with %s has terminated with error code %s\n" "${target}" "${volume_name}" "${tool}" "${rc}" 295 | printf "# It is expected to encounter errors while copying some files. Please check '%s'\n\n" "${ERR_FILE}" 296 | fi 297 | } 298 | 299 | ####################################### 300 | # Detach the attached sparse image from the host. 301 | # Arguments: 302 | # volume_name, the volume name of the attached sparse image. 303 | # Outputs: 304 | # Writes the output of the detach command to stdout. 305 | ####################################### 306 | detach_image() { 307 | local volume_name="${1}" 308 | printf "# Detaching %s. Output: \n" "${volume_name}" 309 | hdiutil detach -force "${volume_name}" && rc=$? || rc=$? 310 | printf "# Detach completed with code %s\n\n" "${rc}" 311 | } 312 | 313 | ####################################### 314 | # Calculate the MD5 hash of the sparse image. 315 | # Arguments: 316 | # destination, the destination folder. 317 | # image_name, the name of the sparse image. 318 | # Outputs: 319 | # Writes the MD5 hash to stdout. 320 | ####################################### 321 | calculate_md5() { 322 | local destination="${1}" 323 | local image_name="${2}" 324 | md5_hash=$(md5 -q "${destination}/${image_name}.sparseimage") 325 | echo "${md5_hash}" 326 | } 327 | 328 | ####################################### 329 | # Calculate the SHA1 hash of the sparse image. 330 | # Arguments: 331 | # destination, the destination folder. 332 | # image_name, the name of the sparse image. 333 | # Outputs: 334 | # Writes the SHA1 hash to stdout. 335 | ####################################### 336 | calculate_sha1() { 337 | local destination="${1}" 338 | local image_name="${2}" 339 | sha1_hash=$(openssl sha1 "${destination}/${image_name}.sparseimage" | awk '{print $2}') 340 | echo "${sha1_hash}" 341 | } 342 | 343 | ####################################### 344 | # Print the summary of the acquisition process. 345 | # Globals: 346 | # LOG_FILE 347 | # ERR_FILE 348 | # Arguments: 349 | # start_datetime, the start date and time of the acquisition process. 350 | # destination, the destination folder. 351 | # image_name, the name of the sparse image. 352 | # hash, the flag to include, if true, the hash values of the sparse image. 353 | # Outputs: 354 | # Writes the summary to stdout. 355 | ####################################### 356 | print_summary() { 357 | local start_datetime="${1}" 358 | local destination="${2}" 359 | local image_name="${3}" 360 | local hash="${4}" 361 | 362 | cat << EOF 363 | # Summary 364 | # ------- 365 | # Start time: ${start_datetime} 366 | # End time: $(date) 367 | # Destination: ${destination} 368 | # Image Name: ${image_name}.sparseimage 369 | EOF 370 | if [[ "${hash}" == "true" ]] 371 | then 372 | printf "# MD5: %s\n" $(calculate_md5 "${destination}" "${image_name}") 373 | printf "# SHA1: %s\n" $(calculate_sha1 "${destination}" "${image_name}") 374 | fi 375 | 376 | printf "\n# Process has completed. Output created in %s\n\n" "${destination}" 377 | printf "# Please check the following log files about the copy:\n# FILES : %s\n# ERRORS: %s\n\n" "${LOG_FILE}" "${ERR_FILE}" 378 | printf "# Thanks for using ASLA - %s\n\n" "${REPO}" 379 | } 380 | 381 | ####################################### 382 | # Print the banner of the script. 383 | ####################################### 384 | print_banner() { 385 | cat << EOF 386 | # 387 | # Apple Silicon Logical Acquisition (ASLA) 388 | # 389 | # GitHub repo: https://github.com/giuseppetotaro/asla 390 | # 391 | 392 | EOF 393 | } 394 | 395 | ####################################### 396 | # Run the acquisition process. 397 | ####################################### 398 | run_process() { 399 | print_acquisition_info "${target}" "${destination}" "${image_name}" "${tool}" 400 | create_sparse_image "${size}" "${destination}" "${image_name}" "${target}" 401 | trap cleanup EXIT 402 | acquire_data "${target}" "${VOLUME_NAME}" "${tool}" 403 | detach_image "${VOLUME_NAME}" 404 | print_summary "${start_datetime}" "${destination}" "${image_name}" "${hash}" 405 | } 406 | 407 | ####################################### 408 | # Main function. 409 | ####################################### 410 | main() { 411 | # Positional arguments 412 | local position=0 # Positional argument counter 413 | local target= 414 | local destination= 415 | 416 | # Keyword arguments 417 | local assisted= 418 | local hash= 419 | local image_name="ACQUISITION" 420 | local size= 421 | local tool="cp" # Default command-line tool for the acquisition 422 | local target_name= 423 | local target_pass= 424 | local target_user= 425 | local nopassword= 426 | 427 | while [[ "${#}" -gt 0 ]] 428 | do 429 | case "${1}" in 430 | -h|--help) 431 | print_usage 432 | exit 0 433 | ;; 434 | -a|--assisted) 435 | assisted="true" 436 | shift 437 | ;; 438 | -c|--calculate-hash) 439 | hash="true" 440 | shift 441 | ;; 442 | -i|--image-name) 443 | image_name="${2:-}" 444 | [[ -z "${image_name}" ]] && printf "%s must have a value\n\n" "${1}" >&2 && print_usage >&2 && exit 1 445 | shift 2 446 | ;; 447 | -n|--name) 448 | target_name="${2:-}" 449 | [[ -z "${target_name}" ]] && printf "%s must have a value\n\n" "${1}" >&2 && print_usage >&2 && exit 1 450 | shift 2 451 | ;; 452 | --no-password) 453 | nopassword="true" 454 | shift 455 | ;; 456 | -p|--password) 457 | target_pass="${2:-}" 458 | [[ -z "${target_pass}" ]] && printf "%s must have a value\n\n" "${1}" >&2 && print_usage >&2 && exit 1 459 | shift 2 460 | ;; 461 | -s|--size) 462 | size="${2}" 463 | shift 2 464 | ;; 465 | -t|--tool) 466 | tool="${2:-}" 467 | [[ -z "${tool}" ]] && printf "%s must have a value\n\n" "${1}" >&2 && print_usage >&2 && exit 1 468 | shift 2 469 | ;; 470 | -u|--user) 471 | target_user="${2:-}" 472 | [[ -z "${target_user}" ]] && printf "%s must have a value\n\n" "${1}" >&2 && print_usage >&2 && exit 1 473 | shift 2 474 | ;; 475 | *) 476 | case "${position}" in 477 | 0) 478 | target="${1}" 479 | position=1 480 | shift 481 | ;; 482 | 1) 483 | destination="${1}" 484 | position=2 485 | shift 486 | ;; 487 | 2) 488 | printf "Unknown argument passed: %s\n\n" "${1}" >&2 489 | print_usage >&2 490 | exit 1 491 | ;; 492 | esac 493 | ;; 494 | esac 495 | done 496 | 497 | # Validation 498 | 499 | [[ -z "${target}" ]] && printf "Requires target folder\n\n" >&2 && print_usage >&2 && exit 1 500 | [[ -z "${destination}" ]] && printf "Requires destination folder\n\n" >&2 && print_usage >&2 && exit 1 501 | [[ ! $(echo "${TOOLS[@]}" | grep -w "${tool}") ]] && printf "Unknown data transfer tool. Only cp and rsync are supported\n\n" >&2 && print_usage >&2 && exit 1 502 | 503 | # Acquisition 504 | 505 | # Discussion: It might be useful to give the option to change the name of log 506 | # files. This can be done with an option for each log file or a single option 507 | # where the same name is used for all log files, which would differ only in 508 | # the file extension. 509 | OUT_FILE="${destination}/${image_name}.out" 510 | LOG_FILE="${destination}/${image_name}.log" 511 | ERR_FILE="${destination}/${image_name}.err" 512 | 513 | clear -x # Clear the screen without attempting to clear the terminal's 514 | # scrollback buffer 515 | print_banner 516 | 517 | backup "${destination}" "${image_name}" 518 | 519 | start_datetime=$(date) 520 | 521 | if [[ ! -d "${target}" || "${assisted}" == "true" ]] 522 | then 523 | while true 524 | do 525 | [[ "${assisted}" == "true" ]] && answer="y" || read -p "# Do you want to continue in assisted mode to identify the target? [yn] " answer 526 | case $answer in 527 | [Yy]) 528 | printf "# Assisted mode selected\n" | tee -a "${OUT_FILE}" 529 | [[ -z "${target_name}" ]] && read -p "# Please provide the computer name of the target: " target_name 530 | [[ -z "${target_user}" ]] && read -p "# Please provide the username of the target: " target_user 531 | if [[ "${nopassword}" != "true" ]] 532 | then 533 | [[ -z "${target_pass}" ]] && read -p "# Please provide the password of the target: " target_pass 534 | fi 535 | mount_shared_disk "${target_name}" "${target_user}" "${target_pass}" "${target}" | tee -a "${OUT_FILE}" 536 | break 537 | ;; 538 | [Nn]) 539 | echo "# Target folder must be provided! Exiting..."; 540 | exit 1 541 | ;; 542 | * ) echo "# Please answer yes or no.";; 543 | esac 544 | done 545 | fi 546 | 547 | run_process | tee -a "${OUT_FILE}" 548 | } 549 | 550 | main "${@:-}" 551 | --------------------------------------------------------------------------------